예제 #1
0
        /// <summary>
        /// Executes the "configure" action
        /// </summary>
        /// <param name="opts">command-line options</param>
        /// <returns>exit code</returns>
        private static int Configure(ConfigureOptions opts)
        {
            var config = File.Exists(opts.ConfigFile)
                ? JiraKanbanConfig.ParseXml(opts.ConfigFile)
                : new JiraKanbanConfig();

            if (opts.JiraUsername != null)
            {
                config.JiraUsername = opts.JiraUsername;
            }
            if (opts.JiraInstanceBaseAddress != null)
            {
                config.JiraInstanceBaseAddress = opts.JiraInstanceBaseAddress;
            }
            if (opts.BoardId != null)
            {
                config.BoardId = opts.BoardId.Value;
            }

            if (!opts.NoStorePassword && !string.IsNullOrWhiteSpace(config.JiraUsername))
            {
                Console.WriteLine($"Enter the Jira password for user '{config.JiraUsername}':");
                config.JiraPassword = GetPassword();
            }

            var xml = config.ToXml();

            using (var f = File.OpenWrite(opts.ConfigFile))
                xml.Save(f);

            Console.WriteLine($"Configuration file generated at: {opts.ConfigFile}");
            return(0);
        }
예제 #2
0
        /// <summary>
        /// Executes the "generate" action
        /// </summary>
        /// <param name="opts">command-line options</param>
        /// <returns>exit code</returns>
        private static int Generate(GenerateOptions opts)
        {
            if (!File.Exists(opts.ConfigFile))
            {
                var color = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"ERROR: Configuration file does not exist: {opts.ConfigFile}");
                Console.ForegroundColor = color;
                return(1);
            }
            var config = JiraKanbanConfig.ParseXml(opts.ConfigFile);

            if (opts.BoardId != null)
            {
                config.BoardId = opts.BoardId.Value;
            }

            var issues = GetIssues(opts, config);

            var charts = KanbanCharts.Create(config, issues);

            SaveChart(opts, charts.FlowEfficiencyChart, "FlowEfficiencyChart.png");
            SaveChart(opts, charts.LeadTimeHistogramChart, "LeadTimeHistogramChart.png");
            SaveChart(opts, charts.WeeklyThroughputHistogramChart, "WeeklyThroughputHistogramChart.png");
            SaveChart(opts, charts.WeeklyThroughputChart, "WeeklyThroughputChart.png");
            SaveChart(opts, charts.LeadTimeControlChart, "LeadTimeControlChart.png");
            SaveChart(opts, charts.CumulativeFlowDiagramChart, "CumulativeFlowDiagramChart.png");

            Console.WriteLine("Success !!!");

            return(0);
        }
예제 #3
0
 /// <summary>
 /// Calculates the "queue time" for this issue, considering the given configuration
 /// </summary>
 /// <param name="config">kanban configuration</param>
 /// <returns>issue queue time, in days</returns>
 public int QueueTime(JiraKanbanConfig config)
 {
     // ReSharper disable PossibleInvalidOperationException
     return((int)Math.Round(Columns
                            .Where(c => c.Entered.HasValue && c.Exited.HasValue)
                            .Where(c => config.QueueColumns.Any(queue => c.Name.Equals(queue, StringComparison.InvariantCultureIgnoreCase)))
                            .Sum(c => (c.Exited.Value - c.Entered.Value).TotalDays)));
     // ReSharper restore PossibleInvalidOperationException
 }
예제 #4
0
        /// <summary>
        /// Calculates the "touch time" for this issue, considering the given configuration
        /// </summary>
        /// <param name="config">kanban configuration</param>
        /// <returns>issue touch time, in days</returns>
        public int TouchTime(JiraKanbanConfig config)
        {
            var leadTime = LeadTime(config);

            if (leadTime == 0)
            {
                return(0);
            }
            var queueTime = Math.Min(leadTime, QueueTime(config));

            return(leadTime - queueTime);
        }
예제 #5
0
        /// <summary>
        /// Calculates the lead time for this issue, considering the given configuration
        /// </summary>
        /// <param name="config">kanban configuration</param>
        /// <returns>issue lead time, in days</returns>
        public int LeadTime(JiraKanbanConfig config)
        {
            var start = Entered(config.CommitmentStartColumns);
            var end   = FirstEntered(config.DoneColumns);

            if (!start.HasValue || !end.HasValue)
            {
                return(0);
            }
            if (end < start)
            {
                end = Entered(config.DoneColumns);
            }
            if (!end.HasValue)
            {
                return(0);
            }
            return((int)Math.Round((end.Value - start.Value).TotalDays));
        }
예제 #6
0
        /// <summary>
        /// Retrieves issue metrics from the local cache or from Jira
        /// </summary>
        /// <param name="opts">command-line options</param>
        /// <param name="config">Kanban configuration</param>
        /// <returns>list of issue metrics</returns>
        private static Issue[] GetIssues(GenerateOptions opts, JiraKanbanConfig config)
        {
            var cacheFile    = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) ?? "./", ".cache");
            var version      = Assembly.GetEntryAssembly().GetName().Version.ToString();
            var cache        = new Cache();
            var quickFilters = config.QuickFilters ?? new int[0];

            Issue[] issues = null;
            if (!opts.NoCache && File.Exists(cacheFile))
            {
                // Attempt to retrieve data from the cache
                try
                {
                    cache = JsonConvert.DeserializeObject <Cache>(File.ReadAllText(cacheFile, new UTF8Encoding(false)));
                }
                catch (Exception e)
                {
                    Console.WriteLine("Error reading cache: " + e.Message);
                }
                if (cache.Version != version)
                {
                    cache = new Cache();
                }

                // Delete old cached items
                var timeLimit = DateTime.UtcNow.AddHours(opts.CacheHours * -1);
                cache.Items.RemoveAll(c => c.TimestampUtc < timeLimit);

                // Try to find a matching cached item
                issues = cache.Items
                         .Where(c => c.BoardId == config.BoardId)
                         .Where(c => c.QuickFilters.SequenceEqual(quickFilters))
                         .Select(c => c.Issues)
                         .FirstOrDefault();
            }

            if (issues != null)
            {
                Console.WriteLine($"Retrieved data from local cache ({issues.Length} issues)");
            }
            else
            {
                // Data is not cached, retrieve it from Jira
                if (config.JiraPassword == null && !string.IsNullOrWhiteSpace(config.JiraUsername))
                {
                    Console.WriteLine($"Enter the Jira password for user '{config.JiraUsername}':");
                    config.JiraPassword = GetPassword();
                }
                using (var extractor = new MetricsExtractor(config, Console.WriteLine))
                {
                    issues = extractor.GetBoardDataAsync(config.BoardId, quickFilters).Result;
                }

                // Add to the cache
                cache.Items.Add(new CacheItem
                {
                    BoardId      = config.BoardId,
                    QuickFilters = quickFilters,
                    Issues       = issues,
                });
            }

            // Save the new cache
            try
            {
                if (!opts.NoCache)
                {
                    using (var f = File.OpenWrite(cacheFile))
                    {
                        var data = new UTF8Encoding(false).GetBytes(JsonConvert.SerializeObject(cache));
                        f.Write(data, 0, data.Length);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Error writing disk cache: " + e.Message);
            }
            return(issues);
        }