From 34a5385c4bd0ba99513040c75a3edea28593890e Mon Sep 17 00:00:00 2001 From: JasonMulligan Date: Sun, 28 Jul 2019 15:27:20 -0400 Subject: [PATCH] Added the Data tab on the Summary Report. --- Models/GeneralReportResult.cs | 1 + ...ryReportResult.cs => SummaryReportData.cs} | 8 +- Models/SummaryReportItemResult.cs | 13 ++ Models/TrelloActionDataDto.cs | 2 + Models/TrelloCardDto.cs | 1 + README.md | 89 +++++++++++- Services/TrelloService.cs | 131 +++++++++++++++--- 7 files changed, 219 insertions(+), 26 deletions(-) rename Models/{SummaryReportResult.cs => SummaryReportData.cs} (52%) create mode 100644 Models/SummaryReportItemResult.cs diff --git a/Models/GeneralReportResult.cs b/Models/GeneralReportResult.cs index 35be5bf..753d852 100644 --- a/Models/GeneralReportResult.cs +++ b/Models/GeneralReportResult.cs @@ -9,6 +9,7 @@ public class GeneralReportResult public string CardTitle { get; set; } public string CardCurrentListId { get; set; } public string CardCurrentListTitle { get; set; } + public string CardUrl { get; set; } public List CardLabels { get; set; } public Dictionary CardMembers { get; set; } public DateTime? CardDateStarted { get; set; } diff --git a/Models/SummaryReportResult.cs b/Models/SummaryReportData.cs similarity index 52% rename from Models/SummaryReportResult.cs rename to Models/SummaryReportData.cs index f1243d5..ee67378 100644 --- a/Models/SummaryReportResult.cs +++ b/Models/SummaryReportData.cs @@ -2,16 +2,16 @@ namespace esdc_sa_appdev_reporting_api.Models { - public class SummaryReportResult + public class SummaryReportData { public List> CompiledResults { get; set; } - public List RawResults { get; set; } + public List ReportItemResults { get; set; } - public SummaryReportResult() + public SummaryReportData() { this.CompiledResults = new List>(); - this.RawResults = new List(); + this.ReportItemResults = new List(); } } } \ No newline at end of file diff --git a/Models/SummaryReportItemResult.cs b/Models/SummaryReportItemResult.cs new file mode 100644 index 0000000..2d0cb68 --- /dev/null +++ b/Models/SummaryReportItemResult.cs @@ -0,0 +1,13 @@ +namespace esdc_sa_appdev_reporting_api.Models +{ + public class SummaryReportItemResult + { + public string TaskName { get; set; } + public string ClientName { get; set; } + public string StatusTitle { get; set; } + public string DateStarted { get; set; } + public string DateCompleted { get; set; } + public string AssignedTo { get; set; } + public string Url { get; set; } + } +} \ No newline at end of file diff --git a/Models/TrelloActionDataDto.cs b/Models/TrelloActionDataDto.cs index 600becb..d98d6c3 100644 --- a/Models/TrelloActionDataDto.cs +++ b/Models/TrelloActionDataDto.cs @@ -3,6 +3,7 @@ namespace esdc_sa_appdev_reporting_api.Models public class TrelloActionDataDto { public TrelloActionDataCardDto card { get; set; } + public TrelloActionDataListDto list { get; set; } public TrelloActionDataListDto listAfter { get; set; } public TrelloActionDataListDto listBefore { get; set; } @@ -10,6 +11,7 @@ public class TrelloActionDataDto public TrelloActionDataDto() { card = new TrelloActionDataCardDto(); + list = new TrelloActionDataListDto(); listAfter = new TrelloActionDataListDto(); listBefore = new TrelloActionDataListDto(); } diff --git a/Models/TrelloCardDto.cs b/Models/TrelloCardDto.cs index 192f322..4aebbc7 100644 --- a/Models/TrelloCardDto.cs +++ b/Models/TrelloCardDto.cs @@ -8,6 +8,7 @@ public class TrelloCardDto public string idBoard { get; set; } public string idList { get; set; } public string name { get; set; } + public string url { get; set; } public List idLabels { get; set; } public List idMembers { get; set; } diff --git a/README.md b/README.md index ff50ef4..1cc7eac 100644 --- a/README.md +++ b/README.md @@ -1 +1,88 @@ -# sa-appdev-reporting \ No newline at end of file +# # ESDC - Senior Advisors - App Dev - Reporting Tool - API + +One Paragraph of project description goes here + +## Getting Started + +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system. + +### Prerequisites + +What things you need to install the software and how to install them + +``` +Give examples +``` + +### Installing + +A step by step series of examples that tell you how to get a development env running + +Say what the step will be + +``` +Give the example +``` + +And repeat + +``` +until finished +``` + +End with an example of getting some data out of the system or using it for a little demo + +## Running the tests + +Explain how to run the automated tests for this system + +### Break down into end to end tests + +Explain what these tests test and why + +``` +Give an example +``` + +### And coding style tests + +Explain what these tests test and why + +``` +Give an example +``` + +## Deployment + +Add additional notes about how to deploy this on a live system + +## Built With + +* [Dropwizard](http://www.dropwizard.io/1.0.2/docs/) - The web framework used +* [Maven](https://maven.apache.org/) - Dependency Management +* [ROME](https://rometools.github.io/rome/) - Used to generate RSS Feeds + +## Contributing + +Please read [CONTRIBUTING.md](https://gist.github.com/PurpleBooth/b24679402957c63ec426) for details on our code of conduct, and the process for submitting pull requests to us. + +## Versioning + +We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/your/project/tags). + +## Authors + +* **Billie Thompson** - *Initial work* - [PurpleBooth](https://github.com/PurpleBooth) + +See also the list of [contributors](https://github.com/your/project/contributors) who participated in this project. + +## License + +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details + +## Acknowledgments + +* Hat tip to anyone whose code was used +* Inspiration +* etc + diff --git a/Services/TrelloService.cs b/Services/TrelloService.cs index 1d78826..c52a734 100644 --- a/Services/TrelloService.cs +++ b/Services/TrelloService.cs @@ -11,7 +11,7 @@ namespace esdc_sa_appdev_reporting_api.Services { public class TrelloService { - public async Task>> GetSummaryReportResults() + public async Task GetSummaryReportResults() { /* The resulting result matrix must have the following format: @@ -28,7 +28,7 @@ where the first row contains a non-empty string as the first item and the client the tallied number of cards per list in order of Backlog to Done. */ - var results = new List>(); + var compiledResults = new List>(); var reportResultItems = await this.GetGeneralReportResults(); @@ -43,16 +43,7 @@ the tallied number of cards per list in order of Backlog to Done. foreach (var result in applicableResults) { - // Get the first green label of the card (should only be only, but just in case, we'll grab the first). - var subCategoryLabel = result.CardLabels.FirstOrDefault(x => x.Color == SolutionConstants.TrelloLabelCategory.Client); - - // Get the label sub-category (meaning, remove the prefix, i.e. "Client: ") - var posLabelSeparator = subCategoryLabel.Name.IndexOf(SolutionConstants.kLabelSeperator); - - var clientKey = (((posLabelSeparator > 0) && (posLabelSeparator < (subCategoryLabel.Name.Length - 1))) - ? subCategoryLabel.Name.Substring(posLabelSeparator + 1) - : subCategoryLabel.Name) - .Trim(); + var clientKey = this.ExtractClientName(result.CardLabels); // Tally the nbr of cards if (compiledResultMatrix.ContainsKey(clientKey) == false) @@ -94,11 +85,11 @@ the tallied number of cards per list in order of Backlog to Done. } } - results.Add(trelloClientValues); + compiledResults.Add(trelloClientValues); } // Order the clients alphabetically - results = results.OrderBy(x => x.FirstOrDefault()).ToList(); + compiledResults = compiledResults.OrderBy(x => x.FirstOrDefault()).ToList(); // Add the first result row. var firstResultSetValues = new List(); @@ -110,9 +101,33 @@ the tallied number of cards per list in order of Backlog to Done. firstResultSetValues.Add(SolutionConstants.TrelloLists.OnHold); firstResultSetValues.Add(SolutionConstants.TrelloLists.Done); - results.Insert(0, firstResultSetValues); + compiledResults.Insert(0, firstResultSetValues); - return results; + // Gather raw results + var reportItemResults = new List(); + + foreach(var card in applicableResults) + { + var reportItemResult = new SummaryReportItemResult(); + + reportItemResult.TaskName = card.CardTitle; + reportItemResult.ClientName = this.ExtractClientName(card.CardLabels); + reportItemResult.StatusTitle = card.CardCurrentListTitle; + reportItemResult.DateStarted = card.CardDateStarted?.ToString(CoreConstants.Formats.DateIso); + reportItemResult.DateCompleted = card.CardDateCompleted?.ToString(CoreConstants.Formats.DateIso); + reportItemResult.AssignedTo = string.Join(", ", card.CardMembers.Select(x => x.Value).ToList()); + reportItemResult.Url = card.CardUrl; + + reportItemResults.Add(reportItemResult); + } + + // Assign result model values + var resultModel = new SummaryReportData(); + + resultModel.CompiledResults = compiledResults; + resultModel.ReportItemResults = reportItemResults; + + return resultModel; } @@ -120,6 +135,8 @@ public async Task> GetGeneralReportResults() { // 1. Gather all cards // 2. Compute date started and completed + // 2.a) Cards that were transferred from Backlog or Committed + // 2.b) Cards that were created directly in either In Progress, On Hold or Done. // 3. Compute number of days on hold var results = new List(); @@ -129,6 +146,7 @@ public async Task> GetGeneralReportResults() var trelloMembers = await this.GetTrelloMembers(); var trelloCards = await this.GetTrelloCards(); var trelloCardMoveActions = await this.GetTrelloCardMoveActions(); + var trelloCardCreatedActions = await this.GetTrelloCardCreateActions(); // Pre-sort trelloCardMoveActions @@ -144,6 +162,7 @@ public async Task> GetGeneralReportResults() result.CardTitle = card.name; result.CardCurrentListId = card.idList; result.CardCurrentListTitle = trelloLists.SingleOrDefault(x => x.id == card.idList)?.name; + result.CardUrl = card.url; foreach (var labelId in card.idLabels) { @@ -173,9 +192,10 @@ public async Task> GetGeneralReportResults() } // Card in backlog and committed aren't considered started. - if ((result.CardCurrentListTitle != SolutionConstants.TrelloLists.Backlog) || + if ((result.CardCurrentListTitle != SolutionConstants.TrelloLists.Backlog) && (result.CardCurrentListTitle != SolutionConstants.TrelloLists.Committed)) { + // 2.a) var action = trelloCardMoveActions .Where ( @@ -183,17 +203,33 @@ public async Task> GetGeneralReportResults() (x.data.card.id == card.id) && (x.data.listAfter.name == SolutionConstants.TrelloLists.InProgress) ) + .OrderBy(x => x.date) .FirstOrDefault(); + // 2.b) + if (action == null) + { + action = trelloCardCreatedActions + .Where(x => x.data.card.id == card.id) + .OrderBy(x => x.date) + .FirstOrDefault(); + } + result.CardDateStarted = action?.date; } // Only cards in done are considered completed. - if (result.CardCurrentListTitle != SolutionConstants.TrelloLists.Done) + if (result.CardCurrentListTitle == SolutionConstants.TrelloLists.Done) { var action = trelloCardMoveActions - .Where(x => (x.data.card.id == card.id) && x.data.listAfter.name == SolutionConstants.TrelloLists.Done) - .LastOrDefault(); + .Where + ( + x => + (x.data.card.id == card.id) && + (x.data.listAfter.name == SolutionConstants.TrelloLists.Done) + ) + .OrderByDescending(x => x.date) + .FirstOrDefault(); result.CardDateCompleted = action?.date; } @@ -317,7 +353,39 @@ public async Task> GetTrelloCards() } - // https://stackoverflow.com/questions/51777063/how-can-i-get-all-actions-for-a-board-using-trellos-rest-api + public async Task> GetTrelloCardCreateActions() + { + // Reference: https://stackoverflow.com/questions/51777063/how-can-i-get-all-actions-for-a-board-using-trellos-rest-api + + using (var http = new HttpClient()) + { + try + { + var url = "https://api.trello.com/1/boards/" + SolutionConstants.kTrelloBoardId + "/actions/" + + "?key=" + SolutionConstants.kTrelloAppKey + + "&token=" + SolutionConstants.kTrelloUserToken + //+ "&before=2019-07-01" + //+ "&since=2019-06-01" + + "&filter=createCard" + + "&limit=1000"; + + var response = await http.GetAsync(url); + + response.EnsureSuccessStatusCode(); + + var jsonResult = await response.Content.ReadAsStringAsync(); + + return JsonConvert.DeserializeObject>(jsonResult); + } + catch (HttpRequestException httpRequestException) + { + Console.WriteLine($"Error in GetTrelloCardMoveActions: {httpRequestException.Message}"); + } + + return null; + } + } + public async Task> GetTrelloCardMoveActions() { @@ -351,5 +419,26 @@ public async Task> GetTrelloCardMoveActions() return null; } } + + + #region --- Private ------------------------------------------------------- + + private string ExtractClientName (List cardLabels) + { + // Get the first green label of the card (should only be only, but just in case, we'll grab the first). + var clientLabel = cardLabels.FirstOrDefault(x => x.Color == SolutionConstants.TrelloLabelCategory.Client); + + // Get the label sub-category (meaning, remove the prefix, i.e. "Client: ") + var posLabelSeparator = clientLabel.Name.IndexOf(SolutionConstants.kLabelSeperator); + + var clientName = (((posLabelSeparator > 0) && (posLabelSeparator < (clientLabel.Name.Length - 1))) + ? clientLabel.Name.Substring(posLabelSeparator + 1) + : clientLabel.Name) + .Trim(); + + return clientName; + } + + #endregion } } \ No newline at end of file