using EgwCoreLib.Utils; using Microsoft.Extensions.Configuration; using MP.Core.DTO; using MP.Data.DbModels.Utils; using MP.Data.DTO; using MP.Data.Repository.Utils; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace MP.Data.Services.Utils { public class StatsDetailService : BaseServ, IStatsDetailService { #region Public Constructors public StatsDetailService( IConfiguration config, IConnectionMultiplexer redis, IStatsDetailRepository repo) : base(config, redis) { _className = "StatsDetail"; _repo = repo; } #endregion Public Constructors #region Public Methods /// public async Task> GetFiltAsync(DateTime dtStart, DateTime dtEnd, string sDest = "", string sType = "") { return await TraceAsync($"{_className}.GetFilt", async (activity) => { string cacheKey = $"{_redisBaseKey}:{_className}:DT:{dtStart:yyyyMMdd}:{dtEnd:yyyyMMdd}"; if (!string.IsNullOrEmpty(sDest)) { cacheKey += $":{sDest}"; } if (!string.IsNullOrEmpty(sType)) { cacheKey += $":{sType}"; } return await GetOrSetCacheAsync( cacheKey, async () => await _repo.GetFiltAsync(dtStart, dtEnd, sDest, sType), LongCache ); }); } /// public async Task> GetParetoAsync(DateTime dtStart, DateTime dtEnd, int maxRec, string sDest = "", string sType = "") { return await TraceAsync($"{_className}.GetFilt", async (activity) => { string cacheKey = $"{_redisBaseKey}:{_className}:PARETO:{dtStart:yyyyMMdd}:{dtEnd:yyyyMMdd}"; if (!string.IsNullOrEmpty(sDest)) { cacheKey += $":{sDest}"; } if (!string.IsNullOrEmpty(sType)) { cacheKey += $":{sType}"; } return await GetOrSetCacheAsync( cacheKey, async () => await _repo.GetParetoAsync(dtStart, dtEnd, maxRec, sDest, sType), LongCache ); }); } /// public async Task> GetParetoStatsDayAsync() { return await TraceAsync($"{_className}.GetDailyParetoStats", async (activity) => { return await GetOrSetCacheAsync( $"{_redisBaseKey}:{_className}:ParetoWeek", async () => await GetParetoDataAsync(), LongCache ); }); } /// public async Task GetRangeAsync(string sEnvir, string sType) { return await TraceAsync($"{_className}.GetRange", async (activity) => { return await GetOrSetCacheAsync( $"{_redisBaseKey}:{_className}:Range:{sEnvir}:{sType}", async () => await _repo.GetRangeAsync(sEnvir, sType), UltraFastCache ); }); } /// public List GetTimeSeriesData(List rawData, bool getCount) { var series = rawData .GroupBy(s => new { s.Destination, s.Type }) // Raggruppiamo per la chiave composta .Select(group => new ChartSeriesDto { // Creiamo un nome leggibile per la legenda del grafico SeriesName = $"{group.Key.Destination}|{group.Key.Type}", // Per ogni gruppo, creiamo la lista dei punti temporali DataPoints = group .OrderBy(p => p.Hour) // Fondamentale: l'asse X deve essere cronologico .Select(p => new chartJsData.chartJsTSerie { x = p.Hour, y = getCount ? p.RequestCount : p.AvgDuration // La metrica richiesta }) .ToList() }) .OrderBy(s => s.SeriesName) // Opzionale: ordina le serie alfabeticamente .ToList(); return series; } /// public async Task ResetCache() { await ClearCacheAsync($"{_redisBaseKey}:*"); } /// public async Task UpsertManyAsync(List listRecords, bool removeOld) { return await TraceAsync($"{_className}.UpsertMany", async (activity) => { string operation = "UpsertMany"; var success = await _repo.UpsertManyAsync(listRecords, removeOld); activity?.SetTag("db.operation", operation); if (success > 0) { await ClearCacheAsync($"{_redisBaseKey}:{_className}:*"); } return success; }); } #endregion Public Methods #region Protected Methods /// /// metodo locale per recupero e trasformazione dati da includere con processo generare di tracking & cache /// /// protected async Task> GetParetoDataAsync() { List result = new(); DateTime adesso = DateTime.Now; var rawData = await GetFiltAsync(adesso.AddDays(-1), adesso); // calcolo le varie statistiche... var pDestRequest = rawData.GroupBy(x => x.Destination) .Select(g => new StatDataDTO { Label = g.Key, Value = g.Sum(x => x.RequestCount) }) .OrderByDescending(x => x.Value) .ToList(); result.Add(new StatInfoDto() { Title = "Dest.Request (#)", DataCollection = pDestRequest, Grouping = StatInfoDto.AggrLevel.Service, Type = StatInfoDto.DataType.Count }); var pDestDuration = rawData.GroupBy(x => x.Destination) .Select(g => new StatDataDTO { Label = g.Key, Value = g.Sum(x => x.RequestCount * x.AvgDuration) }) .OrderByDescending(x => x.Value) .ToList(); result.Add(new StatInfoDto() { Title = "Dest.Duration (ms)", DataCollection = pDestDuration, Grouping = StatInfoDto.AggrLevel.Service, Type = StatInfoDto.DataType.AvgDuration }); var pTypeRequest = rawData.GroupBy(x => x.Type) .Select(g => new StatDataDTO { Label = g.Key, Value = g.Sum(x => x.RequestCount) }) .OrderByDescending(x => x.Value) .ToList(); result.Add(new StatInfoDto() { Title = "Type.Request (#)", DataCollection = pTypeRequest, Grouping = StatInfoDto.AggrLevel.Method, Type = StatInfoDto.DataType.Count }); var pTypeDuration = rawData.GroupBy(x => x.Type) .Select(g => new StatDataDTO { Label = g.Key, Value = g.Sum(x => x.RequestCount * x.AvgDuration) }) .OrderByDescending(x => x.Value) .ToList(); result.Add(new StatInfoDto() { Title = "Type.Duration (ms)", DataCollection = pTypeDuration, Grouping = StatInfoDto.AggrLevel.Method, Type = StatInfoDto.DataType.AvgDuration }); return result; } #endregion Protected Methods #region Private Fields private readonly string _className; private readonly IStatsDetailRepository _repo; #endregion Private Fields } }