예제 #1
0
        public ProjectEventStatsResult GetProjectEventStatsByDay(string projectId, TimeSpan utcOffset, DateTime?localStartDate = null, DateTime?localEndDate = null, DateTime?retentionStartDate = null, bool includeHidden = false, bool includeFixed = false, bool includeNotFound = true)
        {
            // TODO: Check to see if we have day stats available for the project, if not, then use minute stats and queue new project offset to be created in a job
            // Round date range to be full days since stats are per day.
            var range = GetDateRange(localStartDate, localEndDate, utcOffset, TimeSpan.FromDays(1));

            if (range.Item1 == range.Item2)
            {
                return(new ProjectEventStatsResult());
            }

            localStartDate = range.Item1;
            localEndDate   = range.Item2;

            var results = _monthProjectStats.GetRange(GetMonthProjectStatsId(localStartDate.Value, utcOffset, projectId), GetMonthProjectStatsId(localEndDate.Value, utcOffset, projectId));

            if (results.Count > 0)
            {
                var firstWithOccurrence = results.OrderBy(r => r.Id).FirstOrDefault(r => r.DayStats.Any(ds => ds.Value.Total > 0));
                if (firstWithOccurrence != null)
                {
                    var firstErrorDate = firstWithOccurrence.GetDateFromDayStatKey(firstWithOccurrence.DayStats.OrderBy(ds => Int32.Parse(ds.Key)).First(ds => ds.Value.Total > 0).Key);
                    if (localStartDate < firstErrorDate)
                    {
                        localStartDate = firstErrorDate;
                    }
                }

                if (!includeHidden)
                {
                    // Remove stats from hidden doc ids.
                    var hiddenIds = _stackRepository.GetHiddenIds(projectId);
                    if (hiddenIds.Length > 0)
                    {
                        DecrementMonthProjectStatsByStackIds(results, hiddenIds);
                    }
                }

                if (!includeNotFound)
                {
                    // Remove stats from not found doc ids.
                    var notFoundIds = _stackRepository.GetNotFoundIds(projectId);
                    if (notFoundIds.Length > 0)
                    {
                        DecrementMonthProjectStatsByStackIds(results, notFoundIds);
                    }
                }

                if (!includeFixed)
                {
                    // Remove stats from not found doc ids.
                    var fixedIds = _stackRepository.GetFixedIds(projectId);
                    if (fixedIds.Length > 0)
                    {
                        DecrementMonthProjectStatsByStackIds(results, fixedIds);
                    }
                }
            }

            if (retentionStartDate.HasValue)
            {
                retentionStartDate = retentionStartDate.Value.Floor(TimeSpan.FromDays(1));
            }

            // Use finer grained minute blocks if the range is less than 5 days.
            if (localEndDate.Value.Subtract(localStartDate.Value).TotalDays < 5)
            {
                return(GetProjectEventStatsByMinuteBlock(projectId, utcOffset, localStartDate, localEndDate, retentionStartDate, includeHidden, includeFixed, includeNotFound));
            }

            var      monthDocIds       = new List <Tuple <int, int> >();
            DateTime currentDay        = localStartDate.Value;
            var      endOfMonthEndDate = new DateTime(localEndDate.Value.Year, localEndDate.Value.Month, DateTime.DaysInMonth(localEndDate.Value.Year, localEndDate.Value.Month));

            while (currentDay <= endOfMonthEndDate)
            {
                monthDocIds.Add(Tuple.Create(currentDay.Year, currentDay.Month));
                currentDay = currentDay.AddMonths(1);
            }

            // Add missing month documents.
            foreach (var monthDocId in monthDocIds)
            {
                if (!results.Any(d => d.Id == GetMonthProjectStatsId(monthDocId.Item1, monthDocId.Item2, utcOffset, projectId)))
                {
                    results.Add(CreateBlankMonthProjectStats(utcOffset, new DateTime(monthDocId.Item1, monthDocId.Item2, 1), projectId));
                }
            }

            // Fill out missing days with blank stats.
            foreach (MonthProjectStats r in results)
            {
                DateTime date        = r.GetDateFromDayStatKey("1");
                int      daysInMonth = DateTime.DaysInMonth(date.Year, date.Month);
                for (int i = 1; i <= daysInMonth; i++)
                {
                    if (!r.DayStats.ContainsKey(i.ToString()))
                    {
                        r.DayStats.Add(i.ToString(), new EventStatsWithStackIds());
                    }
                }
            }

            var days = new List <KeyValuePair <DateTime, EventStatsWithStackIds> >();

            days = results.Aggregate(days, (current, result) => current.Concat(result.DayStats.ToDictionary(kvp => result.GetDateFromDayStatKey(kvp.Key), kvp => kvp.Value)).ToList())
                   .Where(kvp => kvp.Key >= localStartDate.Value && kvp.Key <= localEndDate.Value).OrderBy(kvp => kvp.Key).ToList();

            int totalLimitedByPlan = retentionStartDate != null && localStartDate < retentionStartDate
                ? days.Where(kvp => kvp.Key < retentionStartDate).SelectMany(kvp => kvp.Value.StackIds.Select(s => s.Key)).Distinct()
                                     .Except(days.Where(kvp => kvp.Key >= retentionStartDate).SelectMany(kvp => kvp.Value.StackIds.Select(s => s.Key)).Distinct())
                                     .Count()
                : 0;

            if (totalLimitedByPlan > 0)
            {
                days = days.Where(kvp => kvp.Key >= retentionStartDate).ToList();
            }

            // Group data points by a time span to limit the number of returned data points.
            TimeSpan groupTimeSpan = TimeSpan.FromDays(1);

            if (days.Count > 50)
            {
                DateTime first = days.Min(m => m.Key);
                DateTime last  = days.Max(m => m.Key);
                TimeSpan span  = last - first;
                groupTimeSpan = TimeSpan.FromDays(((int)Math.Round((span.TotalDays / 50) / 1.0)) * 1);
            }

            var stats = new ProjectEventStatsResult {
                TotalLimitedByPlan = totalLimitedByPlan,
                StartDate          = localStartDate.Value,
                EndDate            = localEndDate.Value,
                Total        = days.Sum(kvp => kvp.Value.Total),
                UniqueTotal  = days.SelectMany(kvp => kvp.Value.StackIds.Select(s => s.Key)).Distinct().Count(),
                NewTotal     = days.Sum(kvp => kvp.Value.NewTotal),
                MostFrequent = new PlanPagedResult <EventStackResult>(days.SelectMany(kvp => kvp.Value.StackIds).GroupBy(kvp => kvp.Key)
                                                                      .Select(v => new EventStackResult {
                    Id    = v.Key,
                    Total = v.Sum(kvp => kvp.Value)
                }).OrderByDescending(s => s.Total).ToList(), totalLimitedByPlan: totalLimitedByPlan, totalCount: days.Count),
                Stats = days.GroupBy(s => s.Key.Floor(groupTimeSpan)).Select(kvp => new DateProjectStatsResult {
                    Date        = kvp.Key,
                    Total       = kvp.Sum(b => b.Value.Total),
                    NewTotal    = kvp.Sum(b => b.Value.NewTotal),
                    UniqueTotal = kvp.Select(b => b.Value.StackIds).Aggregate((current, result) => Merge(result, current)).Count()
                }).ToList()
            };

            return(stats);
        }