/// <summary>
        /// Formula:
        /// DefectDensity(changed) = (DefectCount * 1000) / LOCChanged;
        ///
        /// Expected data table format:
        /// {
        ///   "datatable": [
        ///     ["Month", "Defects/KLOCC"],
        ///     ["2016-01", 0.1],
        ///     ["2016-02", 0.034]
        ///   ]
        /// }
        /// </summary>
        private DataTable DefectDensityChangedByMonth(List <ReviewRecord> source)
        {
            DataTable dataTable = new DataTable();

            dataTable.AddColumn(new Column("string", "Month"));
            dataTable.AddColumn(new Column("number", "Defects/KLOCC"));

            var query = source
                        .GroupBy(record => record.ReviewCreationYear + "-" + record.ReviewCreationMonth)
                        .OrderBy(group => group.Key)
                        .Select(group => new { ProductName = group.Key, Records = group.ToList() });


            foreach (var item in query)
            {
                ReviewsStatistics stat = new ReviewsStatistics(item.Records);
                var totalDefectCount   = stat.TotalDefectCount;
                var totalLOCChanged    = stat.TotalLOCChanged;

                double density = 0;
                if (totalLOCChanged != 0)
                {
                    density = Math.Round((double)(totalDefectCount * 1000) / totalLOCChanged, decimalPlaces);
                }

                Row row = new Row(item.ProductName, density);
                dataTable.AddRow(row);
            }

            return(dataTable);
        }
        /// <summary>
        /// Formula:
        /// InspectionRate = (LOCC) / (TotalPersonTime * 1000)
        ///
        /// Expected data table format:
        /// {
        ///   "datatable": [
        ///     ["Month", "KLOCC/Hour"],
        ///     ["2016-01", 0.1],
        ///     ["2016-02", 0.034]
        ///   ]
        /// }
        /// </summary>
        /// <param name="settingsKey"></param>
        private DataTable InspectionRateByMonth(List <ReviewRecord> source)
        {
            DataTable dataTable = new DataTable();

            dataTable.AddColumn(new Column("string", "Month"));
            dataTable.AddColumn(new Column("number", "KLOCC/Hour"));

            var query = source
                        .GroupBy(record => record.ReviewCreationYear + "-" + record.ReviewCreationMonth)
                        .OrderBy(group => group.Key)
                        .Select(group => new { ProductName = group.Key, Records = group.ToList() });

            foreach (var item in query)
            {
                ReviewsStatistics stat = new ReviewsStatistics(item.Records);
                double            totalPersonTimeInHour = stat.TotalPersonTimeInSecond / (60 * 60);

                double inspectionRate = 0;
                if (totalPersonTimeInHour != 0)
                {
                    inspectionRate = Math.Round(stat.TotalLOCChanged / (totalPersonTimeInHour * 1000), decimalPlaces);
                }

                Row row = new Row(item.ProductName, inspectionRate);
                dataTable.AddRow(row);
            }

            return(dataTable);
        }
        /// <summary>
        /// Formula:
        /// DefectDensity(changed) = (DefectCount * 1000) / LOCChanged;
        ///
        /// Expected data table format:
        /// {
        ///   "datatable": [
        ///     ["Product", "Defects/KLOCC"],
        ///     ["Team1", 0.1],
        ///     ["Team2", 0.034]
        ///   ]
        /// }
        /// </summary>
        public void GenerateDefectDensityChangedByProduct()
        {
            log.Info("Generating: Code defect density (changed) by product ...");

            DataTable dataTable = new DataTable();

            dataTable.AddColumn(new Column("string", "Product"));
            dataTable.AddColumn(new Column("number", "Defects/KLOCC"));

            var query = EagleEyeSettingsReader.Settings.Products
                        .GroupJoin(
                GetValidRecords(),
                product => product,
                record => record.CreatorProductName,
                (product, matching) => new { ProductName = product, Records = matching.ToList() }
                );

            foreach (var item in query)
            {
                ReviewsStatistics stat = new ReviewsStatistics(item.Records);
                var totalDefectCount   = stat.TotalDefectCount;
                var totalLOCChanged    = stat.TotalLOCChanged;

                double density = 0;
                if (totalLOCChanged != 0)
                {
                    density = Math.Round((double)(totalDefectCount * 1000) / totalLOCChanged, decimalPlaces);
                }

                Row row = new Row(item.ProductName, density);
                dataTable.AddRow(row);
            }

            EagleEyePlatformApi.EditDataTable(EagleEyeSettingsReader.Settings.CodeDefectDensityChanged.ChartId, dataTable);

            log.Info("Generating: Code defect density (changed) by product ... Done");
        }