< Summary

Information
Class: OzonOrdersWeb.WebServices.TransactionTimeTracking.TransactionTimeTrackingReportBuilder
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/TransactionTimeTracking/TransactionTimeTrackingReportBuilder.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 125
Coverable lines: 125
Total lines: 190
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 56
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
BuildSummary(...)0%812280%
BuildChartData(...)0%812280%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/TransactionTimeTracking/TransactionTimeTrackingReportBuilder.cs

#LineLine coverage
 1using System.Globalization;
 2using OzonDomains.Models;
 3using OzonOrdersWeb.Areas.Studio2.ViewModels.TransactionTimeTracking;
 4
 5namespace OzonOrdersWeb.WebServices.TransactionTimeTracking;
 6
 7public static class TransactionTimeTrackingReportBuilder
 8{
 9    private const int MaxCalendarDaysInChart = 31;
 10
 11    public static TransactionTimeTrackingReportSummaryViewModel BuildSummary(
 12        List<TransactionTimeTrackingResult> rows)
 13    {
 014        var summary = new TransactionTimeTrackingReportSummaryViewModel
 015        {
 016            CompletedSessions = rows.Count
 017        };
 18
 019        if (rows.Count == 0)
 020            return summary;
 21
 022        summary.UniqueUsers = rows.Select(x => x.UserName).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().Count();
 023        summary.UniqueTransactionTypes = rows
 024            .Select(x => x.TransactionType)
 025            .Where(x => !string.IsNullOrWhiteSpace(x))
 026            .Distinct()
 027            .Count();
 28
 029        summary.TotalOrders = rows.Sum(x => x.OrdersCountFinal);
 030        summary.TotalDurationSeconds = rows.Sum(x => (long)x.DurationSeconds);
 31
 032        summary.MinDurationSeconds = rows.Min(x => x.DurationSeconds);
 033        summary.MaxDurationSeconds = rows.Max(x => x.DurationSeconds);
 34
 035        if (summary.CompletedSessions > 0)
 36        {
 037            summary.AvgDurationSeconds = summary.TotalDurationSeconds / (double)summary.CompletedSessions;
 038            summary.AvgOrdersPerSession = summary.TotalOrders / (double)summary.CompletedSessions;
 39        }
 40
 041        var throughputRows = rows
 042            .Where(x => x.DurationSeconds > 0 && x.OrdersCountFinal > 0)
 043            .Select(x => x.OrdersCountFinal / (x.DurationSeconds / 60d))
 044            .ToList();
 45
 046        summary.SessionsWithThroughput = throughputRows.Count;
 047        if (throughputRows.Count > 0)
 48        {
 049            summary.SimpleAvgOrdersPerMinute = throughputRows.Average();
 050            summary.MinOrdersPerMinute = throughputRows.Min();
 051            summary.MaxOrdersPerMinute = throughputRows.Max();
 52        }
 53
 054        if (summary.TotalDurationSeconds > 0 && summary.TotalOrders > 0)
 055            summary.WeightedAvgOrdersPerMinute = summary.TotalOrders / (summary.TotalDurationSeconds / 60d);
 56
 057        summary.TopUsersBySessions = rows
 058            .GroupBy(x => x.UserName ?? string.Empty)
 059            .Select(g => new TransactionTimeTrackingUserStatRow
 060            {
 061                UserName = string.IsNullOrWhiteSpace(g.Key) ? "—" : g.Key,
 062                Sessions = g.Count(),
 063                TotalOrders = g.Sum(x => x.OrdersCountFinal),
 064                TotalDurationSeconds = g.Sum(x => (long)x.DurationSeconds),
 065                AvgOrdersPerMinute = g.Sum(x => (long)x.DurationSeconds) > 0 && g.Sum(x => x.OrdersCountFinal) > 0
 066                    ? g.Sum(x => x.OrdersCountFinal) / (g.Sum(x => (long)x.DurationSeconds) / 60d)
 067                    : null
 068            })
 069            .OrderByDescending(x => x.Sessions)
 070            .ThenByDescending(x => x.TotalOrders)
 071            .Take(10)
 072            .ToList();
 73
 074        summary.ByTransactionType = rows
 075            .GroupBy(x => x.TransactionType ?? string.Empty)
 076            .Select(g => new TransactionTimeTrackingTypeStatRow
 077            {
 078                TransactionType = string.IsNullOrWhiteSpace(g.Key) ? "—" : g.Key,
 079                Sessions = g.Count(),
 080                TotalOrders = g.Sum(x => x.OrdersCountFinal),
 081                TotalDurationSeconds = g.Sum(x => (long)x.DurationSeconds),
 082                AvgOrdersPerMinute = g.Sum(x => (long)x.DurationSeconds) > 0 && g.Sum(x => x.OrdersCountFinal) > 0
 083                    ? g.Sum(x => x.OrdersCountFinal) / (g.Sum(x => (long)x.DurationSeconds) / 60d)
 084                    : null
 085            })
 086            .OrderByDescending(x => x.Sessions)
 087            .ToList();
 88
 089        return summary;
 90    }
 91
 92    public static TransactionTimeTrackingChartsViewModel BuildChartData(List<TransactionTimeTrackingResult> rows)
 93    {
 094        var vm = new TransactionTimeTrackingChartsViewModel
 095        {
 096            HourLabels = Enumerable.Range(0, 24).Select(h => $"{h:00}:00").ToList(),
 097            HourlyTotalAllTypes = Enumerable.Repeat(0, 24).ToList(),
 098            WeekdayTotalAllTypes = Enumerable.Repeat(0, 7).ToList(),
 099            CalendarDaysLimit = MaxCalendarDaysInChart
 0100        };
 101
 0102        if (rows.Count == 0)
 0103            return vm;
 104
 0105        var types = rows
 0106            .Select(x => string.IsNullOrWhiteSpace(x.TransactionType) ? "—" : x.TransactionType)
 0107            .Distinct()
 0108            .OrderBy(x => x)
 0109            .ToList();
 0110        vm.TransactionTypes = types;
 0111        foreach (var t in types)
 112        {
 0113            vm.TypeDisplayNames[t] = TransactionTimeTrackingChartStyle.GetChartDisplayName(t);
 0114            vm.TypeChartColors[t] = TransactionTimeTrackingChartStyle.GetChartColorHex(t);
 115        }
 116
 0117        var hourlyByType = types.ToDictionary(t => t, _ => Enumerable.Repeat(0, 24).ToArray());
 0118        var weekdayByType = types.ToDictionary(t => t, _ => Enumerable.Repeat(0, 7).ToArray());
 0119        var dailyBuckets = new Dictionary<string, Dictionary<string, int[]>>();
 120
 0121        foreach (var row in rows)
 122        {
 0123            var type = string.IsNullOrWhiteSpace(row.TransactionType) ? "—" : row.TransactionType;
 0124            var msk = MoscowTimeHelper.UtcToMsk(row.CompletedAtUtc);
 0125            var h = msk.Hour;
 0126            var wd = ((int)msk.DayOfWeek + 6) % 7;
 0127            var dateKey = msk.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
 128
 0129            vm.HourlyTotalAllTypes[h]++;
 0130            hourlyByType[type][h]++;
 0131            vm.WeekdayTotalAllTypes[wd]++;
 0132            weekdayByType[type][wd]++;
 133
 0134            if (!dailyBuckets.TryGetValue(dateKey, out var byType))
 135            {
 0136                byType = types.ToDictionary(t => t, _ => Enumerable.Repeat(0, 24).ToArray());
 0137                dailyBuckets[dateKey] = byType;
 138            }
 139
 0140            if (!byType.ContainsKey(type))
 0141                byType[type] = Enumerable.Repeat(0, 24).ToArray();
 142
 0143            byType[type][h]++;
 144        }
 145
 0146        foreach (var t in types)
 147        {
 0148            vm.HourlyByTypeAggregate[t] = hourlyByType[t].ToList();
 0149            vm.WeekdayByType[t] = weekdayByType[t].ToList();
 150        }
 151
 0152        var orderedDates = dailyBuckets.Keys.OrderBy(x => x).ToList();
 0153        vm.CalendarDaysTotalInFilter = orderedDates.Count;
 0154        if (orderedDates.Count > MaxCalendarDaysInChart)
 0155            orderedDates = orderedDates.Skip(orderedDates.Count - MaxCalendarDaysInChart).ToList();
 156
 0157        vm.CalendarDaysShown = orderedDates.Count;
 0158        var ru = CultureInfo.GetCultureInfo("ru-RU");
 159
 0160        foreach (var dk in orderedDates)
 161        {
 0162            if (DateTime.TryParseExact(dk, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var pars
 0163                vm.DateDisplayLabels[dk] = $"{parsedDay:dd.MM.yyyy}, {parsedDay.ToString("dddd", ru)}";
 164            else
 0165                vm.DateDisplayLabels[dk] = dk;
 166
 0167            var dto = new TransactionTimeTrackingHourlyDayDto { DateKey = dk };
 0168            foreach (var kv in dailyBuckets[dk])
 0169                dto.ByTypeHours[kv.Key] = kv.Value.ToList();
 170
 0171            vm.HourlyByCalendarDay.Add(dto);
 172
 0173            var dayTypeCounts = new Dictionary<string, int>();
 0174            foreach (var t in types)
 175            {
 0176                if (!dailyBuckets[dk].TryGetValue(t, out var arr))
 177                    continue;
 0178                dayTypeCounts[t] = arr.Sum();
 179            }
 180
 0181            vm.CountsByCalendarDay.Add(new TransactionTimeTrackingDayTypeCountDto
 0182            {
 0183                DateKey = dk,
 0184                ByType = dayTypeCounts
 0185            });
 186        }
 187
 0188        return vm;
 189    }
 190}