< Summary

Information
Class: AspNetCoreGeneratedDocument.Areas_Studio2_Views_Shared__StatisticsPartial
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/Shared/_StatisticsPartial.cshtml
Line coverage
0%
Covered lines: 0
Uncovered lines: 29
Coverable lines: 29
Total lines: 142
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 10
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ExecuteAsync()0%7280%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/Shared/_StatisticsPartial.cshtml

#LineLine coverage
 1@using Services.HangfireService.Models
 2@model IEnumerable<JobDto>
 3
 4@{
 05var methodColors = new Dictionary<string, string>();
 06var colorPalette = new[] {
 07"#4e79a7", "#f28e2c", "#e15759", "#76b7b2", "#59a14f",
 08"#edc948", "#b07aa1", "#ff9da7", "#9c755f", "#bab0ab"
 09};
 010int colorIndex = 0;
 11
 012foreach (var method in Model
 013.Where(j => !string.IsNullOrEmpty(j.MethodName))
 014.Select(j => j.MethodName)
 015.Distinct())
 16{
 017methodColors[method!] = colorPalette[colorIndex % colorPalette.Length];
 018colorIndex++;
 19}
 20
 021var chartData = Model
 022.Where(j => j.SucceededAt.HasValue && j.Duration.HasValue)
 023.OrderBy(j => j.SucceededAt)
 024.Select(j => new {
 025time = j.SucceededAt!.Value.ToString("yyyy-MM-ddTHH:mm:ss"), // ISO формат для корректной обработки в JS
 026durationMs = j.Duration!.Value,
 027durationMin = j.Duration!.Value / 60000.0,
 028method = j.MethodName,
 029color = methodColors[j.MethodName!]
 030})
 031.ToList();
 32
 033var jsonData = System.Text.Json.JsonSerializer.Serialize(chartData);
 34}
 35
 036@if (chartData.Any())
 37{
 38<div class="hq-chart-wrap">
 39    <canvas id="jobChart" style="height: 400px !important; width: 100%;"></canvas>
 40</div>
 41
 042@if (methodColors.Any())
 43{
 44    <div class="mt-2">
 45        <div class="small text-secondary fw-semibold text-uppercase" style="letter-spacing:.04em;">Легенда методов</div>
 46        <ul class="hq-legend-list">
 047            @foreach (var kv in methodColors)
 48            {
 49                <li>
 50                    <span class="hq-legend-swatch" style="background:@kv.Value;"></span>
 051                    <span>@kv.Key</span>
 52                </li>
 53            }
 54        </ul>
 55    </div>
 56}
 57
 58<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
 59<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.0"></script>
 60
 61<script>
 062    const rawData = @Html.Raw(jsonData);
 63
 64    const labels = rawData.map(d => new Date(d.time).toLocaleString());
 65    const data = {
 66        labels: labels,
 67        datasets: [{
 68            label: 'Длительность (мин)',
 69            data: rawData.map(d => d.durationMin),
 70            backgroundColor: rawData.map(d => d.color),
 71            hoverOffset: 4
 72        }]
 73    };
 74
 75    const config = {
 76        type: 'bar',
 77        data: data,
 78        maintainAspectRatio: false,
 79        options: {
 80            responsive: true,
 81            plugins: {
 82                tooltip: {
 83                    callbacks: {
 84                        label: function (context) {
 85                            const index = context.dataIndex;
 86                            const method = rawData[index].method;
 87                            const durationMin = rawData[index].durationMin.toFixed(2);
 88                            const durationMs = rawData[index].durationMs;
 89                            return `${method}: ${durationMin} мин (${durationMs} мс)`;
 90                        }
 91                    }
 92                },
 93                zoom: {
 94                    zoom: {
 95                        wheel: { enabled: true },
 96                        pinch: { enabled: true },
 97                        mode: 'x',
 98                    },
 99                    pan: {
 100                        enabled: true,
 101                        mode: 'x',
 102                    },
 103                    limits: {
 104                        x: { min: 0, max: rawData.length - 1 },
 105                    }
 106                },
 107                legend: {
 108                    display: false
 109                }
 110            },
 111            scales: {
 112                x: {
 113                    title: {
 114                        display: true,
 115                        text: 'Время завершения'
 116                    },
 117                    ticks: {
 118                        maxRotation: 45,
 119                        minRotation: 45
 120                    }
 121                },
 122                y: {
 123                    title: {
 124                        display: true,
 125                        text: 'Длительность (минуты)'
 126                    }
 127                }
 128            }
 129        }
 130    };
 131
 132    const ctx = document.getElementById('jobChart').getContext('2d');
 133    new Chart(ctx, config);
 134</script>
 135}
 136else
 137{
 138    <div class="hq-empty-state">
 139        <i class="fas fa-chart-bar text-secondary"></i>
 140        <span>Недостаточно данных для графика — нужны succeeded-задачи с длительностью.</span>
 141    </div>
 142}

Methods/Properties

ExecuteAsync()