using Microsoft.AspNetCore.Components; using MP.Core.DTO; using MP.Data; using MP.Data.DbModels.Utils; using MP.Data.DTO; using MP.Data.Services.Utils; namespace MP.IOC.Components.Pages { public partial class CallStats { #region Protected Methods protected override async Task OnInitializedAsync() { await ReloadData(); } #endregion Protected Methods #region Private Fields private List currData = new(); private string currDetail = ""; private string currHistId = ""; private string currId = ""; private string currPieId = ""; private string currTitle = ""; private string currTsId = ""; private List lineTitles = new List(); private Dictionary> ParetoDay = new(); private List tsData = new List(); private List tsDataDetail = new(); private List> TSDataMulti = new(); #endregion Private Fields #region Private Properties /// /// Genera colori sfondo 33% rosso / arancione / giallo /// /// private List bgColors { get => GetSemaforicColors(currData.Count, "0.3"); } private List bgColorsMLine { get => GetDistinctColors(tsDataDetail.Count, "0.3"); } private List DatiPareto { get => currData.Where(x => x.Value > 10).OrderByDescending(x => x.Value).Take(ParetoTake).Select(x => x.Value).ToList(); } private List LabelPareto { get => currData.Where(x => x.Value > 10).OrderByDescending(x => x.Value).Take(ParetoTake).Select(x => x.Label).ToList(); } private int ParetoTake { get => currData.Count() <= 30 ? currData.Count() : 30; } private List LabelPlot { get => tsData.Select(r => $"{r.x:yyyy-MM-dd}").ToList(); } /// /// Genera colori sfondo 33% rosso / arancione / giallo /// /// private List lineColors { get => GetSemaforicColors(currData.Count, "1"); } private List lineColorsMLine { get => GetDistinctColors(tsDataDetail.Count, "1"); } [Inject] private IStatsDetailService SDetService { get; set; } = null!; #endregion Private Properties #region Private Methods private string CheckSelect(string curKey) { return !string.IsNullOrEmpty(currId) && currId == curKey ? "active" : ""; } private void DoReset() { currId = ""; currHistId = ""; currPieId = ""; currTsId = ""; currData = new(); } private void DoSelect(string reqKey) { if (ParetoDay.ContainsKey(reqKey)) { currId = reqKey; currHistId = $"Bar_{reqKey}"; currPieId = $"Pie_{reqKey}"; currTsId = $"TS_{reqKey}"; currTitle = $"Pareto | {reqKey}"; currData = ParetoDay[reqKey]; tsDataDetail = new(); } } private async Task ReloadData() { ParetoDay = await SDetService.GetParetoStatsDayAsync(); } /// /// Genera colori sfondo 33% rosso / arancione / giallo /// /// /// /// private List semaphColors(int numRecords, string alpha) { List answ = new List(); // verde... for (int i = 0; i < numRecords / 3; i++) { answ.Add($"rgba(54, 235, 82, {alpha})"); } // arancione for (int i = 0; i < numRecords / 3; i++) { answ.Add($"rgba(255, 206, 86, {alpha})"); } while (answ.Count < numRecords) { answ.Add($"rgba(255, 99, 132, {alpha}"); } return answ; } private List GetSemaforicColors(int numRecords, string alpha) { List colors = new List(); if (numRecords <= 0) return colors; if (numRecords == 1) { colors.Add($"rgba(54, 235, 82, {alpha})"); return colors; } // Definiamo i punti chiave (R, G, B) (int r, int g, int b) green = (54, 235, 82); (int r, int g, int b) yellow = (255, 206, 86); (int r, int g, int b) red = (255, 99, 132); for (int i = 0; i < numRecords; i++) { // t va da 0.0 (primo record) a 1.0 (ultimo record) double t = (double)i / (numRecords - 1); int r, g, b; if (t < 0.5) { // Da Verde a Giallo (mappiamo 0->0.5 su 0->1) double localT = t * 2; r = (int)(green.r + (yellow.r - green.r) * localT); g = (int)(green.g + (yellow.g - green.g) * localT); b = (int)(green.b + (yellow.b - green.b) * localT); } else { // Da Giallo a Rosso (mappiamo 0.5->1 su 0->1) double localT = (t - 0.5) * 2; r = (int)(yellow.r + (red.r - yellow.r) * localT); g = (int)(yellow.g + (red.g - yellow.g) * localT); b = (int)(yellow.b + (red.b - yellow.b) * localT); } colors.Add($"rgba({r}, {g}, {b}, {alpha})"); } return colors; } //private List GetDistinctColors(int numRecords, string alpha) //{ // List colors = new List(); // for (int i = 0; i < numRecords; i++) // { // // Distribuiamo la tonalità (Hue) uniformemente sui 360 gradi // double hue = (double)i * 360 / numRecords; // // Usiamo il formato CSS hsla() che è più semplice da generare direttamente // // Saturazione 70% e Luminosità 50-60% di solito danno colori vivaci e distinti // colors.Add($"hsla({hue:0.##}, 70%, 50%, {alpha})"); // } // return colors; //} private List GetDistinctColors(int numRecords, string alpha) { List colors = new List(); if (numRecords <= 0) return colors; // Partiamo dal Blu (240°) invece che dal Rosso (0°) double startHue = 240.0; for (int i = 0; i < numRecords; i++) { // Distribuiamo la tonalità aggiungendo l'offset iniziale // L'operatore % 360 assicura di rimanere nel cerchio se superiamo il rosso double hue = (startHue + ((double)i * 360 / numRecords)) % 360; // Strategia di distinzione (attiva sempre, ma più efficace sopra i 5 colori) // Alterniamo i valori per i record pari/dispari string saturation = "70%"; string lightness = "50%"; if (numRecords > 5) { // Se i colori sono molti, alterniamo luminosità e saturazione // I record dispari saranno più chiari e saturi, i pari più scuri e tenui lightness = (i % 2 == 0) ? "45%" : "65%"; saturation = (i % 2 == 0) ? "80%" : "60%"; } colors.Add($"hsla({hue:0.##}, {saturation}, {lightness}, {alpha})"); } return colors; } /// /// abilita visualizzaione grafico dettagli x metodo indicato /// /// private async Task ShowDetail(string selDetail) { currDetail = selDetail; // recupero dettaglio 7gg... DateTime adesso = DateTime.Now; List rawData = await SDetService.GetFiltAsync(adesso.AddDays(-5), adesso, "", selDetail); // conversione con grouping tsData = rawData.Select(r => new chartJsData.chartJsTSerie() { x = r.Hour, y = (double)r.AvgDuration }) .OrderBy(o => o.x) .ToList(); tsDataDetail = SDetService.GetTimeSeriesData(rawData); lineTitles = tsDataDetail.Select(x => x.SeriesName).ToList(); TSDataMulti = tsDataDetail.Select(x => x.DataPoints).ToList(); } #endregion Private Methods } }