public void PushToGoogleSpreadsheet(BoardStatsAnalysis boardStatsAnalysis, string spreadsheetName)
 {
     var listFeed = _googleClient.GetListFeedForSpreadsheet(spreadsheetName);
     AddTitleCard(boardStatsAnalysis, listFeed);
     AddGoodCards(boardStatsAnalysis, listFeed);
     AddBadCards(boardStatsAnalysis, listFeed);
 }
        public BoardStatsAnalysis BuildBoardStatsAnalysis(TrelloData trelloData, List<TimesheetData> timesheetData)
        {
            var boardStats = new BoardStats();
            BuildCardStats(trelloData, boardStats);
            boardStats.ListStats = GetListStats(trelloData.ListsToCount);
            boardStats.TimesheetData = timesheetData;
            boardStats.ProjectStartDate = ProjectStartDate;
            var boardStatsAnalysis = new BoardStatsAnalysis(_configuration, boardStats);

            boardStatsAnalysis.NextMilestoneProjection = BuildProjectionsForNextMilestone(trelloData, boardStatsAnalysis);
            boardStatsAnalysis.Projections = BuildProjections(trelloData, boardStatsAnalysis);

            if (trelloData.MilestoneList != null)
            {
                var milestones = new List<Milestone>();
                foreach (var card in trelloData.MilestoneList.CardDataCollection)
                {

                    if (card.Card.Due.HasValue)
                    {
                        milestones.Add(new Milestone() { Name = card.Card.Name, TargetDate = card.Card.Due.Value });
                    }
                }
                boardStatsAnalysis.Milestones = milestones;
            }

            return boardStatsAnalysis;
        }
 private void AddBadCards(BoardStatsAnalysis boardStatsAnalysis, ListFeed listFeed)
 {
     if (boardStatsAnalysis.BoardStats.BadCardStats.Count > 0)
     {
         var errorRow = _spreadsheetEntryFactory.GetBadCardEntry(boardStatsAnalysis);
         _googleClient.Insert(listFeed, errorRow);
     }
 }
        private void AddGoodCards(BoardStatsAnalysis boardStatsAnalysis, ListFeed listFeed)
        {
            foreach (var dayGroups in boardStatsAnalysis.CompletedCardStats.GroupBy(b => b.DoneAction.DateInTimeZone(_configuration.TimeZone).ToShortDateString()))
            {
                var dayGroupList = dayGroups.ToList();
                for (int i = 0; i < dayGroupList.Count(); i++)
                {
                    var cardStat = dayGroupList[i];
                    var minutesConfig = i * _configuration.TimelineJsOffsetMinutesPerCard;
                    var timeOffset = new TimeSpan(0, minutesConfig, 0);

                    var row = _spreadsheetEntryFactory.GetCompletedCardEntry(cardStat, timeOffset);
                    _googleClient.Insert(listFeed, row);
                }
            }
        }
        public void CreateJsonData(BoardStatsAnalysis boardStatsAnalysis)
        {
            dynamic data = new ExpandoObject();
            data.weeklyStats = CreateWeeklyStatsSeriesCollection(boardStatsAnalysis);

            data.burndown = new ExpandoObject();
            var burndownPointsData = GetBurndownData(boardStatsAnalysis);
            var historicalPointsSeries = CreateSeries("Historical", burndownPointsData);
            data.burndown.historicalPoints = historicalPointsSeries;
            AddProjectionsSeries(boardStatsAnalysis.Projections, data.burndown);

            data.nextMilestoneBurndown = new ExpandoObject();
            AddProjectionsSeries(boardStatsAnalysis.NextMilestoneProjection, data.nextMilestoneBurndown);

            AddHourTimesheetData(boardStatsAnalysis, data);

            data.milestoneSeries = GetMilestonesSeries(boardStatsAnalysis);

            var json = JsonConvert.SerializeObject(data, Newtonsoft.Json.Formatting.Indented, new JavaScriptDateTimeConverter());

            var jsonFileInfo = new FileInfo(Path.Combine(_configuration.WebsiteOutputFolder,_configuration.WebsiteJsonFilename));
            File.WriteAllText(jsonFileInfo.FullName, "var data = " + json);

            var htmlFileInfo = new FileInfo(Path.Combine(_configuration.WebsiteOutputFolder, _configuration.WebsiteHtmlFilename));
            var htmlSourceFileInfo = new FileInfo("Html\\index.html");

            var html = File.ReadAllText(htmlSourceFileInfo.FullName);
            html = html.Replace("[[summary]]",_htmlFactory.GetSummaryTextForBoardStat(boardStatsAnalysis));
            html = html.Replace("[[extra_lists_stats_table]]", _htmlFactory.GetExtraListsStatsTable(boardStatsAnalysis));
            html = html.Replace("[[weekly_stats_rows]]", _htmlFactory.GetWeeklyStatsRows(boardStatsAnalysis));
            html = html.Replace("[[projections_summary]]", _htmlFactory.GetProjectionsSummaryText(boardStatsAnalysis.Projections));
            html = html.Replace("[[next_milestone_projections_summary]]", _htmlFactory.GetProjectionsSummaryText(boardStatsAnalysis.NextMilestoneProjection));

            File.WriteAllText(htmlFileInfo.FullName, html);

            CopyFileToOutputFolder("style.css");
            CopyFileToOutputFolder("bootstrap.min.css");
        }
        private BoardProjections BuildBoardProjection(BoardStatsAnalysis boardStatsAnalysis, double estimatedPoints)
        {
            var totalDonePoints = boardStatsAnalysis.TotalPoints;
            var elapsedWeeks = boardStatsAnalysis.CompletedWeeksElapsed - _configuration.WeeksToSkipForVelocityCalculation;

            var historicalPointsPerWeek = totalDonePoints / elapsedWeeks;
            var projectedWeeksToComplete = estimatedPoints / historicalPointsPerWeek;
            var projectedWeeksMin = projectedWeeksToComplete * _configuration.TrelloProjectionsEstimateWindowLowerBoundFactor;
            var projectedWeeksMax = projectedWeeksToComplete * _configuration.TrelloProjectionsEstimateWindowUpperBoundFactor;

            var projection = new BoardProjections()
            {
                EstimatePoints = estimatedPoints,
                TotalPointsCompleted = totalDonePoints,
                elapsedWeeks = elapsedWeeks,
                historicalPointsPerWeek = historicalPointsPerWeek,
                ProjectedWeeksToCompletion = projectedWeeksToComplete,
                ProjectionCompletionDate = GetCompletionDate(projectedWeeksToComplete),
                ProjectedMinimumCompletionDate = GetCompletionDate(projectedWeeksMin),
                ProjectedMaximumCompletionDate = GetCompletionDate(projectedWeeksMax)
            };
            return projection;
        }
        private void AddHourTimesheetData(BoardStatsAnalysis boardStatsAnalysis, dynamic data)
        {
            var burndownHoursData = GetBurndownHoursData(boardStatsAnalysis);
            var burndownHoursSeries = CreateSeries("HistoricalHours", burndownHoursData);
            data.historicalHours = burndownHoursSeries;

            var burndownExcludedHoursData = GetBurndownExcludedHoursData(boardStatsAnalysis);
            var burndownExcludedHoursSeries = CreateSeries("HistoricalExcludedHours", burndownExcludedHoursData);
            data.historicalExcludedHours = burndownExcludedHoursSeries;
        }
        private List<dynamic> GetMilestonesSeries(BoardStatsAnalysis boardStatsAnalysis)
        {
            var totalPoints = boardStatsAnalysis.TotalPoints + boardStatsAnalysis.EstimatedListPoints;
            List<Milestone> milestones = boardStatsAnalysis.Milestones;
            var milestoneSeries = new List<dynamic>();
            foreach (var milestone in milestones)
            {

                var topPoint = GetDateValuePoint(milestone.TargetDate, totalPoints);
                topPoint.milestoneName = milestone.Name;
                topPoint.isTopPoint = true;
                var bottomPoint = GetDateValuePoint(milestone.TargetDate, 0);
                bottomPoint.milestoneName = milestone.Name;
                bottomPoint.isTopPoint = false;

                var series = CreateSeries(milestone.Name + "(Milestone)", new List<dynamic>() { bottomPoint, topPoint });
                series.color = "#AFAFC1";
                series.marker = new ExpandoObject();
                series.marker.enabled = false;
                milestoneSeries.Add(series);
            }
            return milestoneSeries;
        }
        private List<dynamic> GetBurndownHoursData(BoardStatsAnalysis boardStatsAnalysis)
        {
            var burndownHoursData = new List<dynamic>();
            foreach (var weekStats in boardStatsAnalysis.WeekStats.Take(boardStatsAnalysis.WeekStats.Count - 1))
            {
                var point = GetDateValuePoint(weekStats.EndDate, weekStats.TotalHours);
                burndownHoursData.Add(point);
            }

            var lastWeekStats = boardStatsAnalysis.WeekStats.Last();
            var lastPoint = GetDateValuePoint(DateTime.Now, lastWeekStats.TotalHours);
            burndownHoursData.Add(lastPoint);
            return burndownHoursData;
        }
        private List<dynamic> GetBurndownData(BoardStatsAnalysis boardStatsAnalysis)
        {
            var totalPoints = boardStatsAnalysis.TotalPoints + boardStatsAnalysis.EstimatedListPoints;
            var totalPointsRemaining = totalPoints;

            var burnDownData = new List<dynamic>();
            foreach (var weekStats in boardStatsAnalysis.WeekStats.Take(boardStatsAnalysis.WeekStats.Count - 1))
            {
                totalPointsRemaining -= weekStats.PointsCompleted;
                var point = GetDateValuePoint(weekStats.EndDate, totalPointsRemaining);
                burnDownData.Add(point);
            }

            var lastWeekStats = boardStatsAnalysis.WeekStats.Last();
            totalPointsRemaining -= lastWeekStats.PointsCompleted;
            var lastPoint = GetDateValuePoint(DateTime.Now, totalPointsRemaining);
            burnDownData.Add(lastPoint);
            return burnDownData;
        }
        private List<dynamic> CreateWeeklyStatsSeriesCollection(BoardStatsAnalysis boardStatsAnalysis)
        {
            var seriesCollection = new System.Collections.Generic.List<dynamic>();

            dynamic series = CreateSeries("In Progress", "In Progress", boardStatsAnalysis.WeekStats.Select(w => w.NumberOfCardsInProgress).ToArray());
            seriesCollection.Add(series);

            series = CreateSeries("In Test", "In Test", boardStatsAnalysis.WeekStats.Select(w => w.NumberOfCardsInTest).ToArray());
            seriesCollection.Add(series);

            series = CreateSeries("Trinity", "Stories Completed", boardStatsAnalysis.WeekStats.Select(w => w.GetNumberOfCardsWithLabel("Trinity")).ToArray());
            seriesCollection.Add(series);

            series = CreateSeries("Classic", "Stories Completed", boardStatsAnalysis.WeekStats.Select(w => w.GetNumberOfCardsWithLabel("Classic")).ToArray());
            seriesCollection.Add(series);

            series = CreateSeries("Implemented Prior to Estimate", "Implemented Prior to Estimate", boardStatsAnalysis.WeekStats.Select(w => w.GetNumberOfCardsWithLabel("Implemented Prior to Estimate")).ToArray());
            seriesCollection.Add(series);

            series = CreateSeries("Hotfix", "Hotfix", boardStatsAnalysis.WeekStats.Select(w => w.GetNumberOfCardsWithLabel("Hotfix")).ToArray());
            seriesCollection.Add(series);

            return seriesCollection;
        }
 private void AddTitleCard(BoardStatsAnalysis boardStatsAnalysis, ListFeed listFeed)
 {
     var titleRow = _spreadsheetEntryFactory.GetTitleCardEntry(boardStatsAnalysis);
     _googleClient.Insert(listFeed, titleRow);
 }
        private BoardProjections BuildProjectionsForNextMilestone(TrelloData trelloData, BoardStatsAnalysis boardStatsAnalysis)
        {
            var estimatedPoints = GetEstimatedPointsNextMilestoneForList(trelloData, _configuration.ListNames.EstimatedList);
            estimatedPoints += GetEstimatedPointsNextMilestoneForList(trelloData, _configuration.ListNames.InProgressListName);
            estimatedPoints += GetEstimatedPointsNextMilestoneForList(trelloData, _configuration.ListNames.InTestListName);

            boardStatsAnalysis.EstimatedListPoints = estimatedPoints;

            var projection = BuildBoardProjection(boardStatsAnalysis, estimatedPoints);
            return projection;
        }