コード例 #1
0
ファイル: TfsQueryService.cs プロジェクト: thnk2wn/TfsStates
        public async Task <TfsQueryResult> Query(TfsKnownConnection knownConn, TfsStatesViewModel model)
        {
            var sw            = Stopwatch.StartNew();
            var vssConnection = knownConn.ToVssConnection();

            this.workItemClient = vssConnection.GetClient <WorkItemTrackingHttpClient>();
            var wiql           = TfsQueryBuilder.BuildQuery(model);
            var tfsQueryResult = await this.workItemClient.QueryByWiqlAsync(new Wiql { Query = wiql });

            var workItems = tfsQueryResult.WorkItems.ToList();

            this.totalCount = workItems.Count();

            var queue        = new ConcurrentQueue <TfsInfo>();
            var asyncOptions = GetAsyncOptions();

            var getRevsBlock = new ActionBlock <WorkItemReference>(
                async workItemRef =>
            {
                var tfsInfo = await ProcessWorkItemRevisions(workItemRef);

                if (tfsInfo.TransitionCount > model.MinTransitions)
                {
                    queue.Enqueue(tfsInfo);
                }
            },
                asyncOptions);

            foreach (var wiRef in workItems)
            {
                getRevsBlock.Post(wiRef);
            }

            getRevsBlock.Complete();
            await getRevsBlock.Completion;

            var list = queue
                       .OrderBy(i => i.Iteration)
                       .ThenBy(i => i.Id)
                       .ToList();

            sw.Stop();

            var result = new TfsQueryResult
            {
                TfsItems       = list,
                TotalWorkItems = this.processedCount,
                TotalRevisions = this.revisionsCount
            };

            return(result);
        }
コード例 #2
0
ファイル: HomeController.cs プロジェクト: thnk2wn/TfsStates
        public async Task <IActionResult> Index()
        {
            var model = new TfsStatesViewModel();

            await LoadLookups(model);

            if (string.IsNullOrEmpty(model.RunReadyState.Message))
            {
                model.RunReadyState.IsReady = true;
            }

            return(View(model));
        }
コード例 #3
0
        public static string BuildQuery(TfsStatesViewModel model)
        {
            // TODO: Make work item type configurable
            var wiql = @"
SELECT [System.Id], [System.Title]
FROM WorkItems
WHERE [System.TeamProject] = '@project'
AND  [Work Item Type] IN ('User Story', 'Bug')";

            // Note: @CurrentIteration doesn't work in API, only TFS portal

            if (!string.IsNullOrEmpty(model.Iteration))
            {
                wiql = wiql + Environment.NewLine +
                       $"AND[System.IterationPath] UNDER '{model.Iteration}'";
            }

            wiql = wiql.Replace("@project", model.Project);

            return(wiql);
        }
コード例 #4
0
ファイル: HomeController.cs プロジェクト: thnk2wn/TfsStates
        private async Task LoadLookups(TfsStatesViewModel model)
        {
            model = model ?? new TfsStatesViewModel();

            if (model.Projects?.Any() ?? false)
            {
                return;
            }

            model.Connections.Add(new SelectListItem
            {
                Text = "- Select connection -"
            });

            var settingsUrl = Url.Action("Index", "Settings");

            model.Projects.Insert(0, NoProjectSelected);

            TfsKnownConnection defaultConnection = null;
            var lastReportRun = await this.reportHistoryService.GetLastRunSettings();

            try
            {
                var connections = await settingsService.GetConnections();

                if (connections == null || (connections.Connections?.Count ?? 0) == 0)
                {
                    model.RunReadyState.NotReady(
                        $"<a href='{settingsUrl}'>TFS settings</a> need to first be supplied.");
                }
                else
                {
                    model.Connections.AddRange(
                        connections
                        .Connections
                        .OrderBy(c => c.Name)
                        .Select(c =>
                                new SelectListItem
                    {
                        Text     = c.Name,
                        Value    = c.Id.ToString(),
                        Selected = (1 == connections.Connections.Count ||
                                    ShouldSelectConnection(c, lastReportRun))
                    }));

                    if (model.ConnectionId == Guid.Empty &&
                        lastReportRun != null &&
                        connections.Connection(lastReportRun.ConnectionId) != null)
                    {
                        defaultConnection = connections.Connection(lastReportRun.ConnectionId);
                    }
                    else if (model.ConnectionId != Guid.Empty)
                    {
                        defaultConnection = connections.Connection(model.ConnectionId);
                    }
                    else if (connections.Connections.Count == 1)
                    {
                        defaultConnection = connections.Connections.First();
                    }
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex);
                model.RunReadyState.NotReady(
                    $"Error loading <a href='{settingsUrl}'>TFS settings</a>. " +
                    $"Adjust settings and try again.");
            }

            if (model.RunReadyState.State == TfsStatesViewModel.RunStates.NotReady)
            {
                return;
            }

            if (defaultConnection != null)
            {
                model.ConnectionId = defaultConnection.Id;

                try
                {
                    var projectNames = await this.projectService.GetProjectNames(defaultConnection);

                    if (projectNames == null || !projectNames.Any())
                    {
                        model.RunReadyState.NotReady(
                            $"No TFS projects found. Check <a href='{settingsUrl}'>TFS settings</a> " +
                            $"and try again.");
                    }
                    else
                    {
                        model.Projects.AddRange(projectNames);
                    }
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex);
                    model.RunReadyState.NotReady(
                        $"Error loading TFS projects. Check <a href='{settingsUrl}'>TFS settings</a> " +
                        $"and your connectivity and try again.");
                }

                if (model.RunReadyState.State == TfsStatesViewModel.RunStates.NotReady)
                {
                    return;
                }
            }


            if (lastReportRun != null && defaultConnection != null)
            {
                if (model.Projects?.Any() ?? false &&
                    lastReportRun.ConnectionId == defaultConnection.Id &&
                    model.Projects.Contains(lastReportRun.Project))
                {
                    model.Project = lastReportRun.Project;
                }
            }

            if (defaultConnection != null &&
                !string.IsNullOrEmpty(model.Project) &&
                model.Project != NoProjectSelected &&
                (!model.Iterations?.Any() ?? false))
            {
                try
                {
                    model.Iterations = await GetIterationData(defaultConnection, model.Project);
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex);
                    model.RunReadyState.NotReady(
                        $"Error loading TFS iterations. Check <a href='{settingsUrl}'>TFS settings</a> " +
                        $"and your connectivity and try again.");
                }
            }

            if (lastReportRun != null)
            {
                if (model.Iterations?.Any() ?? false && model.Iterations.Contains(lastReportRun.Iteration))
                {
                    model.Iteration = lastReportRun.Iteration;
                }
            }
        }
コード例 #5
0
ファイル: HomeController.cs プロジェクト: thnk2wn/TfsStates
        public async Task <IActionResult> RunReport(TfsStatesViewModel viewModel)
        {
            if (viewModel.ConnectionId == Guid.Empty)
            {
                ModelState.AddModelError(nameof(viewModel.ConnectionId), "Connection is required");
            }

            if (string.IsNullOrEmpty(viewModel.Project) || viewModel.Project == NoProjectSelected)
            {
                ModelState.AddModelError(nameof(viewModel.Project), "Project is required");
            }

            if (string.IsNullOrEmpty(viewModel.Iteration) || viewModel.Iteration == NoIterationSelected)
            {
                ModelState.AddModelError(
                    nameof(viewModel.Iteration),
                    "Iteration Under is required (you can select a parent iteration container)");
            }

            await LoadLookups(viewModel);

            if (!ModelState.IsValid)
            {
                return(View(ViewName, viewModel));
            }

            var sw = Stopwatch.StartNew();
            await FileUtility.Cleanup();

            TfsQueryResult queryResult = null;

            var knownConn = await this.settingsService.GetConnection(viewModel.ConnectionId);

            try
            {
                SendProgress($"Querying project {viewModel.Project}, iteration under {viewModel.Iteration}...");
                queryResult = await this.tfsQueryService.Query(knownConn, viewModel);
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex);
                var settingsUrl = Url.Action("Index", "Settings");
                viewModel.RunReadyState.NotReady(
                    $"Error querying TFS. Verify <a href='{settingsUrl}'>TFS settings</a> " +
                    $"and your connectivity and try again.");
                return(View(ViewName, viewModel));
            }

            if (queryResult.TfsItems.Count > 0)
            {
                var fName    = $"TfsStates_{DateTime.Now.Ticks}.xlsx";
                var filename = await FileUtility.GetFilename(fName);

                SendProgress($"Writing {filename}...");

                var projectUrl = $"{knownConn.Url}/{viewModel.Project}";
                this.excelWriterService.Write(filename, queryResult.TfsItems, projectUrl);

                SendProgress($"Launching {filename}...");
                System.Threading.Thread.Sleep(1000);

                viewModel.ResultFilename = fName;

                await Electron.Shell.OpenExternalAsync(filename);
            }

            // eat file in use exception
            try
            {
                await this.reportHistoryService.Record(knownConn.Id, viewModel.Project, viewModel.Iteration);
            }
            catch (IOException ioEx) { }

            if (queryResult.TfsItems.Count > 0)
            {
                var chart = chartService.CreateBarChart(queryResult);
                ViewData["chart"] = chart;
            }

            sw.Stop();
            var items            = queryResult.TfsItems;
            var totalTransitions = items.Sum(x => x.TransitionCount);
            var avgTransitions   = items.Count > 0
                ? Math.Round(items.Average(x => x.TransitionCount), 0)
                : 0;

            viewModel.FinalProgress = new ReportProgress
            {
                WorkItemsProcessed = queryResult.TotalWorkItems,
                Message            = $"Processed {"work item".ToQuantity(queryResult.TotalWorkItems, "###,##0")} and " +
                                     $"{"revision".ToQuantity(queryResult.TotalRevisions, "###,##0")} in {sw.Elapsed.Humanize()}. " +
                                     $"{"transition".ToQuantity(totalTransitions, "###,##0")}. " +
                                     $"Average work item transitions: {avgTransitions}"
            };

            return(View(ViewName, viewModel));
        }