From 1413e80f094e46130c631031c0fe81a8dc00d277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D0=BB=D0=B5=D0=BA?= =?UTF-8?q?=D1=81=D0=B0=D0=BD=D0=B4=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Mon, 24 Jul 2023 11:14:07 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=BE=D1=80=D0=BC=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=81=D1=83=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D0=BE=D1=82=D1=87=D1=91=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Добавил сервис для работы с суточными отчётами. 2. Сделал формирование отчёта в Excel файле по заготовленному шаблону. 3. Поправил Dto. 4. Реализация методов в контроллере + небольшое исправление. В методе получения списка добавил запрос, который позволяет получать данные по параметрам. --- .../AutoGeneratedDailyReportDto.cs | 7 +- .../LimitingParameterRecordDto.cs | 5 + .../AutoGeneratedDailyReportRequest.cs | 19 ++ .../IAutoGeneratedDailyReportMakerService.cs | 20 ++ .../IAutoGeneratedDailyReportService.cs | 36 +++ .../AsbCloudInfrastructure.csproj | 1 + AsbCloudInfrastructure/DependencyInjection.cs | 7 +- .../AutoGeneratedDailyReportMakerService.cs | 67 +++++ .../AutoGeneratedDailyReportService.cs | 242 ++++++++++++++++++ .../HeadExcelBlockWriter.cs | 37 +++ .../IExcelBlockWriter.cs | 9 + .../LimitingParameterExcelBlockWriter.cs | 31 +++ .../SubsystemExcelBlockWriter.cs | 31 +++ .../TimeBalanceExcelBlockWriter.cs | 38 +++ .../AutogeneratedDailyReportTemplate.xlsx | Bin 0 -> 9272 bytes .../AutoGeneratedDailyReportController.cs | 57 ++++- 16 files changed, 592 insertions(+), 15 deletions(-) create mode 100644 AsbCloudApp/Requests/AutoGeneratedDailyReportRequest.cs create mode 100644 AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportMakerService.cs create mode 100644 AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs create mode 100644 AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportMakerService.cs create mode 100644 AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs create mode 100644 AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs create mode 100644 AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/IExcelBlockWriter.cs create mode 100644 AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs create mode 100644 AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/SubsystemExcelBlockWriter.cs create mode 100644 AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/TimeBalanceExcelBlockWriter.cs create mode 100644 AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportTemplate.xlsx diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs index d24be197..cad747c8 100644 --- a/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace AsbCloudApp.Data.AutogeneratedDailyReport; @@ -31,15 +30,15 @@ public class AutoGeneratedDailyReportDto /// /// Блок подсистем /// - public IEnumerable Subsystems { get; set; } = null!; + public SubsystemRecordDto[] Subsystems { get; set; } = null!; /// /// Блок ограничивающих параметров /// - public IEnumerable LimitingParameters { get; set; } = null!; + public LimitingParameterRecordDto[] LimitingParameters { get; set; } = null!; /// /// Баланс времени /// - public IEnumerable TimeBalance { get; set; } = null!; + public TimeBalanceRecordDto[] TimeBalance { get; set; } = null!; } \ No newline at end of file diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs index 67172684..7dc87b05 100644 --- a/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs @@ -10,6 +10,11 @@ public class LimitingParameterRecordDto /// public double TotalHours { get; set; } + /// + /// Проходка + /// + public double Depth { get; set; } + /// /// Название ограничивающего параметра /// diff --git a/AsbCloudApp/Requests/AutoGeneratedDailyReportRequest.cs b/AsbCloudApp/Requests/AutoGeneratedDailyReportRequest.cs new file mode 100644 index 00000000..5e7dae7c --- /dev/null +++ b/AsbCloudApp/Requests/AutoGeneratedDailyReportRequest.cs @@ -0,0 +1,19 @@ +using System; + +namespace AsbCloudApp.Requests; + +/// +/// Параметры запроса для получения авто-генерируемых суточных отчётов +/// +public class AutoGeneratedDailyReportRequest : RequestBase +{ + /// + /// Дата начала периода + /// + public DateOnly? StartDate { get; set; } + + /// + /// Дата конца периода + /// + public DateOnly? FinishDate { get; set; } +} \ No newline at end of file diff --git a/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportMakerService.cs b/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportMakerService.cs new file mode 100644 index 00000000..8571e356 --- /dev/null +++ b/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportMakerService.cs @@ -0,0 +1,20 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using AsbCloudApp.Data.AutogeneratedDailyReport; + +namespace AsbCloudApp.Services.AutoGeneratedDailyReports; + +/// +/// Сервис для генерации файлов авто-генерируемых суточный отчётов +/// +public interface IAutoGeneratedDailyReportMakerService +{ + /// + /// Генерация файла + /// + /// + /// + /// + Task MakeReportAsync(AutoGeneratedDailyReportDto report, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs b/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs new file mode 100644 index 00000000..b5a4d5ee --- /dev/null +++ b/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using AsbCloudApp.Data; +using AsbCloudApp.Data.AutogeneratedDailyReport; +using AsbCloudApp.Requests; + +namespace AsbCloudApp.Services.AutoGeneratedDailyReports; + +/// +/// Сервис для работы с авто-генерируемыми суточными отчётами +/// +public interface IAutoGeneratedDailyReportService +{ + /// + /// Список файлов суточных отчётов + /// + /// + /// + /// + /// + Task> GetListAsync(int idWell, + AutoGeneratedDailyReportRequest request, + CancellationToken cancellationToken); + + /// + /// Генерация файла с отчётом + /// + /// + /// + /// + /// + Task<(string fileName, Stream stream)> GenerateReportAsync(int idWell, DateOnly reportDate, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj index e6a5edcb..d807a6c8 100644 --- a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj +++ b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj @@ -36,6 +36,7 @@ + diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index ed88ec5f..601d05e7 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -23,7 +23,9 @@ using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; +using AsbCloudApp.Services.AutoGeneratedDailyReports; using AsbCloudApp.Services.Notifications; +using AsbCloudInfrastructure.Services.AutoGeneratedDailyReports; namespace AsbCloudInfrastructure { @@ -215,7 +217,10 @@ namespace AsbCloudInfrastructure services.AddTransient, WitsRecordRepository>(); services.AddTransient, WitsRecordRepository>(); services.AddTransient, WitsRecordRepository>(); - services.AddTransient, WitsRecordRepository>(); + services.AddTransient, WitsRecordRepository>(); + + services.AddTransient(); + services.AddTransient(); return services; } diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportMakerService.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportMakerService.cs new file mode 100644 index 00000000..0f1c7076 --- /dev/null +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportMakerService.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using AsbCloudApp.Data.AutogeneratedDailyReport; +using AsbCloudApp.Services.AutoGeneratedDailyReports; +using AsbCloudInfrastructure.Services.AutoGeneratedDailyReports.AutogeneratedDailyReportBlocks; +using ClosedXML.Excel; + +namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports; + +public class AutoGeneratedDailyReportMakerService : IAutoGeneratedDailyReportMakerService +{ + private readonly IEnumerable blockWriters = new List() + { + new HeadExcelBlockWriter(), + new SubsystemExcelBlockWriter(), + new LimitingParameterExcelBlockWriter(), + new TimeBalanceExcelBlockWriter() + }; + + public async Task MakeReportAsync(AutoGeneratedDailyReportDto report, CancellationToken cancellationToken) + { + using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken); + + using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled); + + AddToWorkbook(workbook, report); + + MemoryStream memoryStream = new MemoryStream(); + workbook.SaveAs(memoryStream, new SaveOptions { }); + memoryStream.Seek(0, SeekOrigin.Begin); + + return memoryStream; + } + + private async Task GetExcelTemplateStreamAsync(CancellationToken cancellationToken) + { + var resourceName = Assembly.GetExecutingAssembly() + .GetManifestResourceNames() + .FirstOrDefault(n => n.EndsWith("AutogeneratedDailyReportTemplate.xlsx"))!; + + using var stream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream(resourceName)!; + + var memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream, cancellationToken); + memoryStream.Position = 0; + + return memoryStream; + } + + private void AddToWorkbook(XLWorkbook workbook, AutoGeneratedDailyReportDto report) + { + const string sheetName = "Рапорт"; + + var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName) + ?? throw new FileFormatException($"Книга excel не содержит листа {sheetName}."); + + foreach (var blockBuilder in blockWriters) + { + blockBuilder.Write(sheet, report); + } + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs new file mode 100644 index 00000000..89f94c43 --- /dev/null +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using AsbCloudApp.Data; +using AsbCloudApp.Data.AutogeneratedDailyReport; +using AsbCloudApp.Data.SAUB; +using AsbCloudApp.Data.Subsystems; +using AsbCloudApp.Exceptions; +using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; +using AsbCloudApp.Services; +using AsbCloudApp.Services.AutoGeneratedDailyReports; +using AsbCloudApp.Services.Subsystems; +using AsbCloudDb.Model; +using AsbCloudInfrastructure.Services.SAUB; + +namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports; + +public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService +{ + private const string fileNameTemplate = "Суточный_отчёт_по_скважине_{0}_куст_{1}_от_{2}.xlsx"; + + private readonly IWellService wellService; + private readonly IWellOperationRepository wellOperationRepository; + private readonly TelemetryDataCache telemetryDataCache; + private readonly ISubsystemOperationTimeService subsystemOperationTimeService; + private readonly ICrudRepository subsystemRepository; + private readonly ILimitingParameterService limitingParameterService; + private readonly IAutoGeneratedDailyReportMakerService autoGeneratedDailyReportMakerService; + + public AutoGeneratedDailyReportService(IWellService wellService, + IWellOperationRepository wellOperationRepository, + TelemetryDataCache telemetryDataCache, + ISubsystemOperationTimeService subsystemOperationTimeService, + ICrudRepository subsystemRepository, + ILimitingParameterService limitingParameterService, + IAutoGeneratedDailyReportMakerService autoGeneratedDailyReportMakerService) + { + this.wellOperationRepository = wellOperationRepository; + this.wellService = wellService; + this.telemetryDataCache = telemetryDataCache; + this.subsystemOperationTimeService = subsystemOperationTimeService; + this.subsystemRepository = subsystemRepository; + this.limitingParameterService = limitingParameterService; + this.autoGeneratedDailyReportMakerService = autoGeneratedDailyReportMakerService; + } + + public async Task> GetListAsync(int idWell, + AutoGeneratedDailyReportRequest request, + CancellationToken cancellationToken) + { + var result = new PaginationContainer + { + Skip = request.Skip ?? 0, + Take = request.Take ?? 10, + }; + + var reports = new List(); + + var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken) + ?? throw new ArgumentInvalidException("Скважина не найдена", nameof(idWell)); + + if (!well.IdTelemetry.HasValue) + throw new ArgumentInvalidException("Телеметрия для скважины отсутствует", nameof(idWell)); + + var datesRange = telemetryDataCache.GetOrDefaultDataDateRange(well.IdTelemetry.Value); + + if (datesRange is null) + return result; + + if (request.StartDate.HasValue) + { + var startDate = new DateTime(request.StartDate.Value.Year, request.StartDate.Value.Month, + request.StartDate.Value.Day); + + datesRange.From = startDate; + } + + if (request.FinishDate.HasValue) + { + var finishDate = new DateTime(request.FinishDate.Value.Year, request.FinishDate.Value.Month, + request.FinishDate.Value.Day); + + datesRange.To = finishDate; + } + + for (var dateFrom = datesRange.From; dateFrom <= datesRange.To; dateFrom = dateFrom.AddDays(1)) + { + reports.Add(new AutoGeneratedDailyReportDto + { + FileName = string.Format(fileNameTemplate, well.Caption, well.Cluster, DateOnly.FromDateTime(dateFrom)), + ReportDate = DateOnly.FromDateTime(dateFrom), + FileSize = GetFileSize() / 1024, + }); + } + + result.Items = reports.Skip(result.Skip).Take(result.Take); + + return result; + } + + public async Task<(string fileName, Stream stream)> GenerateReportAsync(int idWell, DateOnly reportDate, + CancellationToken cancellationToken) + { + var startDate = new DateTime(reportDate.Year, reportDate.Month, reportDate.Day); + var finishDate = startDate.AddDays(1); + + var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken) + ?? throw new ArgumentInvalidException("Скважина не найдена", nameof(idWell)); + + if (!well.IdTelemetry.HasValue) + throw new ArgumentInvalidException("Телеметрия для скважины отсутствует", nameof(idWell)); + + var factOperations = (await GetFactOperationsAsync(well.Id, startDate, finishDate, + cancellationToken)).ToArray(); + + var report = new AutoGeneratedDailyReportDto + { + FileName = string.Format(fileNameTemplate, well.Caption, well.Cluster, reportDate), + FileSize = GetFileSize() / 1024, + ReportDate = reportDate, + Head = CreateHeadBlock(well, reportDate, factOperations), + Subsystems = (await CreateSubsystemBlockAsync(idWell, startDate, finishDate, cancellationToken)).ToArray(), + LimitingParameters = (await CreateLimitingParameterBlockAsync(idWell, startDate, finishDate, cancellationToken)).ToArray(), + TimeBalance = factOperations.GroupBy(w => w.CategoryName).Select(x => new TimeBalanceRecordDto + { + Name = x.Key ?? "Название операции отсутствует", + DurationHours = x.Sum(o => o.DurationHours) + }).ToArray(), + }; + + var stream = await autoGeneratedDailyReportMakerService.MakeReportAsync(report, cancellationToken); + + return (report.FileName, stream); + } + + private HeadBlockDto CreateHeadBlock(WellDto well, DateOnly reportDate, WellOperationDto[] factOperations) + { + var customer = well.Companies.FirstOrDefault(company => company.IdCompanyType == 1); + + return new HeadBlockDto + { + From = reportDate, + Customer = customer?.Caption ?? string.Empty, + Deposit = well.Deposit ?? string.Empty, + ClusterName = well.Cluster ?? string.Empty, + WellName = well.Caption, + WellDepthIntervalStartDate = factOperations.FirstOrDefault()?.DepthStart ?? 0.00, + WellDepthIntervalFinishDate = factOperations.LastOrDefault()?.DepthEnd ?? 0.00 + }; + } + + private async Task> CreateSubsystemBlockAsync(int idWell, DateTime startDate, + DateTime finishDate, + CancellationToken cancellationToken) + { + var subsystems = await subsystemRepository.GetAllAsync(cancellationToken); + var subsystemStats = await GetSubsystemStatsAsync(idWell, startDate, finishDate, + cancellationToken); + + return subsystems.Select(subsystem => + { + var subsytemStat = subsystemStats?.FirstOrDefault(s => s.IdSubsystem == subsystem.Id); + + return new SubsystemRecordDto + { + Name = subsystem.Name, + KUsage = subsytemStat?.KUsage ?? 0.00, + UsedTimeHours = subsytemStat?.UsedTimeHours ?? 0.00, + Depth = subsytemStat?.SumDepthInterval ?? 0.00, + }; + }); + } + + private async Task> CreateLimitingParameterBlockAsync(int idWell, + DateTime startDate, DateTime finishDate, CancellationToken cancellationToken) + { + var limitingParameterStats = (await GetLimitingParameterStatsAsync(idWell, + startDate, finishDate, cancellationToken)).ToArray(); + + var sumDepths = limitingParameterStats.Sum(x => x.Depth); + + return limitingParameterStats.Select(l => new LimitingParameterRecordDto + { + NameFeedRegulator = l.NameFeedRegulator, + TotalHours = l.TotalMinutes, + PercentDepth = l.Depth / sumDepths, + Depth = l.Depth, + }); + } + + private Task> GetFactOperationsAsync(int idWell, DateTime startDate, + DateTime finishDate, CancellationToken cancellationToken) + { + var request = new WellOperationRequest + { + IdWell = idWell, + OperationType = WellOperation.IdOperationTypeFact, + GeDate = startDate, + LtDate = finishDate, + SortFields = new[] { "DateStart asc" }, + }; + + return wellOperationRepository.GetAsync(request, cancellationToken); + } + + private Task?> GetSubsystemStatsAsync(int idWell, DateTime startDate, + DateTime finishDate, CancellationToken cancellationToken) + { + var request = new SubsystemOperationTimeRequest + { + IdWell = idWell, + GtDate = startDate, + LtDate = finishDate, + }; + + return subsystemOperationTimeService.GetStatAsync(request, cancellationToken); + } + + private Task> GetLimitingParameterStatsAsync(int idWell, + DateTime startDate, DateTime finishDate, CancellationToken cancellationToken) + { + var request = new LimitingParameterRequest + { + IdWell = idWell, + GtDate = startDate, + LtDate = finishDate, + }; + + return limitingParameterService.GetStatAsync(request, cancellationToken); + } + + private int GetFileSize() + { + const int fileSizeTemplate = 8192; + + return new Random().Next(1, 8193) + fileSizeTemplate; + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs new file mode 100644 index 00000000..a81f7c08 --- /dev/null +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs @@ -0,0 +1,37 @@ +using AsbCloudApp.Data.AutogeneratedDailyReport; +using ClosedXML.Excel; + +namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports.AutogeneratedDailyReportBlocks; + +public class HeadExcelBlockWriter : IExcelBlockWriter +{ + private const int columnCustomer = 1; + private const int columnDeposit = 2; + private const int columnCluster = 3; + private const int columnWell = 4; + + private const int columnFrom = 1; + private const int columnTo = 2; + private const int columnWellDepthIntervalStartDate = 3; + private const int columnWellDepthIntervalFinishDate = 4; + + public void Write(IXLWorksheet sheet, AutoGeneratedDailyReportDto report) + { + const int rowHeaderBlockSectionOne = 2; + const int rowHeaderBlockSectionTwo = 5; + + var rowSectionOne = sheet.Row(1 + rowHeaderBlockSectionOne); + + rowSectionOne.Cell(columnCustomer).Value = report.Head.Customer; + rowSectionOne.Cell(columnDeposit).Value = report.Head.Deposit; + rowSectionOne.Cell(columnCluster).Value = report.Head.ClusterName; + rowSectionOne.Cell(columnWell).Value = report.Head.WellName; + + var rowSectionTwo = sheet.Row(1 + rowHeaderBlockSectionTwo); + + rowSectionTwo.Cell(columnFrom).Value = report.Head.From; + rowSectionTwo.Cell(columnTo).Value = report.Head.To; + rowSectionTwo.Cell(columnWellDepthIntervalStartDate).Value = report.Head.WellDepthIntervalStartDate; + rowSectionTwo.Cell(columnWellDepthIntervalFinishDate).Value = report.Head.WellDepthIntervalFinishDate; + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/IExcelBlockWriter.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/IExcelBlockWriter.cs new file mode 100644 index 00000000..0f83fe05 --- /dev/null +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/IExcelBlockWriter.cs @@ -0,0 +1,9 @@ +using AsbCloudApp.Data.AutogeneratedDailyReport; +using ClosedXML.Excel; + +namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports.AutogeneratedDailyReportBlocks; + +public interface IExcelBlockWriter +{ + void Write(IXLWorksheet sheet, AutoGeneratedDailyReportDto report); +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs new file mode 100644 index 00000000..b2edb32c --- /dev/null +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs @@ -0,0 +1,31 @@ +using System.Linq; +using AsbCloudApp.Data.AutogeneratedDailyReport; +using ClosedXML.Excel; + +namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports.AutogeneratedDailyReportBlocks; + +public class LimitingParameterExcelBlockWriter : IExcelBlockWriter +{ + private const int rowHeaderBlock = 18; + + private const int columnNameFeedRegulator = 1; + private const int columnDepth = 2; + private const int columnTotalHours = 3; + private const int columnPercentDepth = 4; + + public void Write(IXLWorksheet sheet, AutoGeneratedDailyReportDto report) + { + if(!report.LimitingParameters.Any()) + return; + + for (int i = 0; i < report.LimitingParameters.Length; i++) + { + var row = sheet.Row(1 + i + rowHeaderBlock); + + row.Cell(columnNameFeedRegulator).Value = report.LimitingParameters[i].NameFeedRegulator; + row.Cell(columnDepth).Value = report.LimitingParameters[i].Depth; + row.Cell(columnTotalHours).Value = report.LimitingParameters[i].TotalHours; + row.Cell(columnPercentDepth).Value = report.LimitingParameters[i].PercentDepth; + } + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/SubsystemExcelBlockWriter.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/SubsystemExcelBlockWriter.cs new file mode 100644 index 00000000..c1e8182c --- /dev/null +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/SubsystemExcelBlockWriter.cs @@ -0,0 +1,31 @@ +using System.Linq; +using AsbCloudApp.Data.AutogeneratedDailyReport; +using ClosedXML.Excel; + +namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports.AutogeneratedDailyReportBlocks; + +public class SubsystemExcelBlockWriter : IExcelBlockWriter +{ + private const int rowHeaderBlock = 10; + + private const int columnName = 1; + private const int columnKUsage = 2; + private const int columnDepth = 3; + private const int columnUsedTimeHours = 4; + + public void Write(IXLWorksheet sheet, AutoGeneratedDailyReportDto report) + { + if(!report.Subsystems.Any()) + return; + + for (int i = 0; i < report.Subsystems.Length; i++) + { + var row = sheet.Row(1 + i + rowHeaderBlock); + + row.Cell(columnName).Value = report.Subsystems[i].Name; + row.Cell(columnKUsage).Value = report.Subsystems[i].KUsage; + row.Cell(columnDepth).Value = report.Subsystems[i].Depth; + row.Cell(columnUsedTimeHours).Value = report.Subsystems[i].UsedTimeHours; + } + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/TimeBalanceExcelBlockWriter.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/TimeBalanceExcelBlockWriter.cs new file mode 100644 index 00000000..c61fed8f --- /dev/null +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/TimeBalanceExcelBlockWriter.cs @@ -0,0 +1,38 @@ +using System.Linq; +using AsbCloudApp.Data.AutogeneratedDailyReport; +using ClosedXML.Excel; + +namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports.AutogeneratedDailyReportBlocks; + +public class TimeBalanceExcelBlockWriter : IExcelBlockWriter +{ + private const int rowHeaderBlock = 27; + + private const int columnName = 1; + private const int columnDurationHours = 2; + + public void Write(IXLWorksheet sheet, AutoGeneratedDailyReportDto report) + { + if(!report.TimeBalance.Any()) + return; + + for (int i = 0; i < report.TimeBalance.Length; i++) + { + var row = sheet.Row(1 + i + rowHeaderBlock); + + row.Cell(columnName).Value = report.TimeBalance[i].Name; + row.Cell(columnDurationHours).Value = report.TimeBalance[i].DurationHours; + + AddBorderToCell(row.Cell(columnName)); + AddBorderToCell(row.Cell(columnDurationHours)); + } + } + + private void AddBorderToCell(IXLCell cell) + { + cell.Style.Border.TopBorder = XLBorderStyleValues.Thin; + cell.Style.Border.BottomBorder = XLBorderStyleValues.Thin; + cell.Style.Border.LeftBorder = XLBorderStyleValues.Thin; + cell.Style.Border.RightBorder = XLBorderStyleValues.Thin; + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportTemplate.xlsx b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportTemplate.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..55c2ae12c2f940d501deb62181e78e8a3c4bd120 GIT binary patch literal 9272 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 -/// Контроллер для автоматически сгенерированных отчётов +/// Контроллер для авто-генерируемых суточных отчётов /// [ApiController] [Route("api/well/{idWell}/[controller]")] [Authorize] public class AutoGeneratedDailyReportController : ControllerBase { + private readonly IAutoGeneratedDailyReportService autoGeneratedDailyReportService; + private readonly IWellService wellService; + + public AutoGeneratedDailyReportController(IAutoGeneratedDailyReportService autoGeneratedDailyReportService, + IWellService wellService) + { + this.autoGeneratedDailyReportService = autoGeneratedDailyReportService; + this.wellService = wellService; + } + /// - /// Метод генерации отчёта + /// Формирование отчёта /// /// Id скважины - /// Дата работы системы + /// Дата отчёта /// /// [HttpPost] [Route("generate")] [ProducesResponseType(typeof(PhysicalFileResult), (int)HttpStatusCode.OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public Task GenerateAsync([FromRoute] int idWell, DateOnly date, CancellationToken cancellationToken) + public async Task GenerateAsync([FromRoute] int idWell, + [Required] DateOnly reportDate, + CancellationToken cancellationToken) { - throw new NotImplementedException(); + if (!await CanUserAccessToWellAsync(idWell, cancellationToken)) + return Forbid(); + + var reportFile = await autoGeneratedDailyReportService.GenerateReportAsync(idWell, + reportDate, + cancellationToken); + + return File(reportFile.stream, "application/octet-stream", reportFile.fileName); } /// - /// Получение списка файлов отчёта + /// Список файлов суточных отчётов /// /// Id скважины - /// Дата начала работ на скважине - /// Дата окончания работ на скважине + /// Параметры запроса /// /// [HttpGet] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public Task GetListAsync([FromRoute] int idWell, DateOnly? begin, DateOnly? end, + public async Task GetListAsync([FromRoute][Required] int idWell, + [FromQuery] AutoGeneratedDailyReportRequest request, CancellationToken cancellationToken) { - throw new NotImplementedException(); + if (!await CanUserAccessToWellAsync(idWell, cancellationToken)) + return Forbid(); + + var reports = await autoGeneratedDailyReportService.GetListAsync(idWell, + request, + cancellationToken); + + return Ok(reports); + } + + private async Task CanUserAccessToWellAsync(int idWell, CancellationToken cancellationToken) + { + int? idCompany = User.GetCompanyId(); + return idCompany is not null && await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, + idWell, cancellationToken).ConfigureAwait(false); } } \ No newline at end of file