using AsbCloudApp.Data; using AsbCloudApp.Data.Progress; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Background; using AsbSaubReport; using Mapster; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services; public class ReportService : IReportService { private readonly IAsbCloudDbContext db; private readonly ITelemetryService telemetryService; private readonly FileService fileService; private readonly IWellService wellService; private readonly BackgroundWorker backgroundWorkerService; public ReportService(IAsbCloudDbContext db, ITelemetryService telemetryService, IWellService wellService, FileService fileService, BackgroundWorker backgroundWorkerService) { this.db = db; this.wellService = wellService; this.backgroundWorkerService = backgroundWorkerService; this.telemetryService = telemetryService; this.fileService = fileService; } public string EnqueueCreateReportWork(int idWell, int idUser, ReportParametersRequest request, Action progressHandler) { var work = new WorkToCreateReport(idWell, idUser, request, progressHandler); work.OnErrorAsync = (message, exception, token) => Task.Run(() => { var state = new ProgressExceptionDto { Operation = "error", Progress = 100f, Message = string.IsNullOrEmpty(message) ? exception.Message : message, Exception = exception, }; progressHandler.Invoke(state, work.Id); }, token); progressHandler.Invoke(new ReportProgressDto { Operation = "Ожидает начала в очереди.", Progress = 0f, }, work.Id); backgroundWorkerService.Enqueue(work); return work.Id; } public int GetReportPagesCount(int idWell, DateTimeOffset begin, DateTimeOffset end, int stepSeconds, int format) { var timezoneOffset = wellService.GetTimezone(idWell).Hours; var beginRemote = begin.DateTime.ToTimeZoneOffsetHours(timezoneOffset); var endRemote = end.DateTime.ToTimeZoneOffsetHours(timezoneOffset); var generator = GetReportGenerator(idWell, beginRemote, endRemote, stepSeconds, format, db); var pagesCount = generator.GetPagesCount(); return pagesCount; } public DatesRangeDto? GetDatesRangeOrDefault(int idWell) { var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); if (telemetry is null) return null; var range = telemetryService.GetDatesRange(telemetry.Id); return range; } public async Task> GetAllReportsByWellAsync(int idWell, CancellationToken token) { var timezoneOffset = wellService.GetTimezone(idWell).Hours; var timeSpan = TimeSpan.FromHours(timezoneOffset); var propertiesQuery = db.ReportProperties.Include(r => r.File) .Where(p => p.IdWell == idWell) .OrderBy(o => o.File.UploadDate) .AsNoTracking() .Take(1024); var entities = await propertiesQuery.ToListAsync(token); var dtos = entities.Select(p => new ReportPropertiesDto { Id = p.Id, Name = p.File.Name, File = new FileInfoDto { Id = p.File.Id, Author = null, IdAuthor = p.File.IdAuthor ?? 0, IdCategory = p.File.IdCategory, IdWell = p.File.IdWell, Name = p.File.Name, Size = p.File.Size, UploadDate = p.File.UploadDate.ToOffset(timeSpan), }, IdWell = p.IdWell, Date = p.File.UploadDate.ToOffset(timeSpan), Begin = p.Begin.ToOffset(timeSpan), End = p.End.ToOffset(timeSpan), Step = p.Step, Format = p.Format == 0 ? ".pdf" : ".las" }); return dtos; } public async Task CreateReportAsync( string workId, int idWell, int idUser, ReportParametersRequest request, Action progressHandler, CancellationToken token) { var timezoneOffset = wellService.GetTimezone(idWell).Hours; var beginRemote = request.Begin.DateTime; var endRemote = request.End.DateTime; var beginUtc = request.Begin.ToUniversalTime(); var endUtc = request.End.ToUniversalTime(); var tempDir = Path.Combine(Path.GetTempPath(), "report"); var generator = GetReportGenerator(idWell, beginRemote, endRemote, request.StepSeconds, request.Format, db); var reportFileName = Path.Combine(tempDir, generator.GetReportDefaultFileName()); var totalPages = generator.GetPagesCount(); generator.OnProgress += (s, e) => { var arg = e.Adapt(); progressHandler(arg, workId); }; generator.Make(reportFileName); var ReportCategoryId = db.FileCategories .AsNoTracking() .First(c => c.Name.Equals("Рапорт")) .Id; var fileInfo = (await fileService.MoveAsync(idWell, idUser, ReportCategoryId, reportFileName, reportFileName, token))!; if (fileInfo == null) { var state = new ProgressExceptionDto { Operation = "error", Progress = 100f, Message = "Не удалось сгенерировать файл отчёта", Exception = new FileNotFoundException(), }; progressHandler(state, workId); return; } progressHandler(new ReportProgressFinalDto() { Operation = "done", Progress = 100f, TotalPages = totalPages, CurrentPage = totalPages, file = fileInfo, }, workId); var newReportProperties = new ReportProperty { IdWell = idWell, IdFile = fileInfo.Id, Begin = beginUtc, End = endUtc, Step = request.StepSeconds, Format = request.Format }; db.ReportProperties.Add(newReportProperties); db.SaveChanges(); } private static IReportGenerator GetReportGenerator(int idWell, DateTime begin, DateTime end, int stepSeconds, int format, IAsbCloudDbContext context) { var dataSource = new ReportDataSourcePgCloud(context, idWell); IReportGenerator generator = format switch { //LAS 1 => new AsbSaubReportLas.ReprotGeneratorLas(dataSource), //PDF _ => new AsbSaubReportPdf.ReprotGeneratorPdf(dataSource), }; if (begin == default || end == default) { var analyzeResult = dataSource.Analyze(); begin = begin == default ? analyzeResult.MinDate : begin; end = end == default ? begin.AddDays(1) : end; } generator.Begin = begin; generator.End = end; generator.Step = TimeSpan.FromSeconds(stepSeconds); generator.WithCharts = true; generator.WithEvents = true; return generator; } public async Task DeleteAllOldReportsAsync(TimeSpan lifetime, CancellationToken token) { var lifeTimeStartDate = DateTimeOffset.UtcNow.Date - lifetime; var fileIds = await db.ReportProperties .Where(r => r.File.UploadDate.Date < lifeTimeStartDate) .Select(r => r.IdFile) .ToArrayAsync(token); return await fileService.DeleteAsync(fileIds, token); } }