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 00000000..55c2ae12 Binary files /dev/null and b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportTemplate.xlsx differ diff --git a/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs b/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs index aeb8b9f7..08e6dc9c 100644 --- a/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs +++ b/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs @@ -1,9 +1,13 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Net; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data.AutogeneratedDailyReport; +using AsbCloudApp.Requests; +using AsbCloudApp.Services; +using AsbCloudApp.Services.AutoGeneratedDailyReports; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -11,42 +15,75 @@ using Microsoft.AspNetCore.Mvc; namespace AsbCloudWebApi.Controllers; /// -/// Контроллер для автоматически сгенерированных отчётов +/// Контроллер для авто-генерируемых суточных отчётов /// [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