Merge pull request 'feature/daily_report' (#84) from feature/daily_report into dev

Reviewed-on: http://test.digitaldrilling.ru:8080/DDrilling/AsbCloudServer/pulls/84
This commit is contained in:
Никита Фролов 2023-07-25 17:39:42 +05:00
commit 963da559e2
20 changed files with 790 additions and 1 deletions

View File

@ -0,0 +1,28 @@
namespace AsbCloudApp.Data.AutogeneratedDailyReport;
/// <summary>
/// DTO авто-сгенерированного суточного отчёта
/// </summary>
public class AutoGeneratedDailyReportDto : AutoGeneratedDailyReportInfoDto
{
/// <summary>
/// Блок заголовка
/// </summary>
public HeadBlockDto Head { get; set; } = null!;
//TODO: поля не должны быть массивами
/// <summary>
/// Блок подсистем
/// </summary>
public SubsystemRecordDto[] Subsystems { get; set; } = null!;
/// <summary>
/// Блок ограничивающих параметров
/// </summary>
public LimitingParameterRecordDto[] LimitingParameters { get; set; } = null!;
/// <summary>
/// Баланс времени
/// </summary>
public TimeBalanceRecordDto[] TimeBalance { get; set; } = null!;
}

View File

@ -0,0 +1,25 @@
using System;
namespace AsbCloudApp.Data.AutogeneratedDailyReport;
/// <summary>
/// Базовая информация о суточном отчёте
/// </summary>
public class AutoGeneratedDailyReportInfoDto
{
/// <summary>
/// Дата формирования отчёта
/// </summary>
public DateOnly ReportDate { get; set; }
/// <summary>
/// Название файла
/// </summary>
public string FileName { get; set; } = null!;
/// <summary>
/// Размер файла
/// </summary>
public int FileSize { get; set; }
}

View File

@ -0,0 +1,37 @@
namespace AsbCloudApp.Data.AutogeneratedDailyReport;
/// <summary>
/// Блок заголовка
/// </summary>
public class HeadBlockDto
{
/// <summary>
/// Название скважины
/// </summary>
public string Well { get; set; } = null!;
/// <summary>
/// Название куста
/// </summary>
public string Cluster { get; set; } = null!;
/// <summary>
/// Заказчик
/// </summary>
public string Customer { get; set; } = null!;
/// <summary>
/// Месторождение
/// </summary>
public string Deposit { get; set; } = null!;
/// <summary>
/// Глубина забоя на дату начала интервала
/// </summary>
public double DepthFrom { get; set; }
/// <summary>
/// Глубина забоя на дату окончания интервала
/// </summary>
public double DepthTo { get; set; }
}

View File

@ -0,0 +1,27 @@
namespace AsbCloudApp.Data.AutogeneratedDailyReport;
/// <summary>
/// Блок ограничивающих параметров
/// </summary>
public class LimitingParameterRecordDto
{
/// <summary>
/// Время использования, мин
/// </summary>
public double Hours { get; set; }
/// <summary>
/// Проходка, м
/// </summary>
public double Depth { get; set; }
/// <summary>
/// Название ограничивающего параметра
/// </summary>
public string NameFeedRegulator { get; set; } = null!;
/// <summary>
/// Процент по проходке, %
/// </summary>
public double PercentDepth { get; set; }
}

View File

@ -0,0 +1,27 @@
namespace AsbCloudApp.Data.AutogeneratedDailyReport;
/// <summary>
/// Блок подсистем
/// </summary>
public class SubsystemRecordDto
{
/// <summary>
/// Название подсистемы
/// </summary>
public string Name { get; set; } = null!;
/// <summary>
/// Использование, %
/// </summary>
public double KUsage { get; set; }
/// <summary>
/// Время работы, ч
/// </summary>
public double UsedTimeHours { get; set; }
/// <summary>
/// Проходка
/// </summary>
public double Depth { get; set; }
}

View File

@ -0,0 +1,17 @@
namespace AsbCloudApp.Data.AutogeneratedDailyReport;
/// <summary>
/// Баланс времени
/// </summary>
public class TimeBalanceRecordDto
{
/// <summary>
/// Название операции
/// </summary>
public string Name { get; set; } = null!;
/// <summary>
/// Продолжительность, часы
/// </summary>
public double DurationHours { get; set; }
}

View File

@ -0,0 +1,19 @@
using System;
namespace AsbCloudApp.Requests;
/// <summary>
/// Параметры запроса для получения авто-генерируемых суточных отчётов
/// </summary>
public class AutoGeneratedDailyReportRequest : RequestBase
{
/// <summary>
/// Дата начала периода
/// </summary>
public DateOnly? StartDate { get; set; }
/// <summary>
/// Дата конца периода
/// </summary>
public DateOnly? FinishDate { get; set; }
}

View File

@ -0,0 +1,20 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.AutogeneratedDailyReport;
namespace AsbCloudApp.Services.AutoGeneratedDailyReports;
/// <summary>
/// Сервис для генерации файлов авто-генерируемых суточный отчётов
/// </summary>
public interface IAutoGeneratedDailyReportMakerService
{
/// <summary>
/// Генерация файла
/// </summary>
/// <param name="report"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<Stream> MakeReportAsync(AutoGeneratedDailyReportDto report, CancellationToken cancellationToken);
}

View File

@ -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;
/// <summary>
/// Сервис для работы с авто-генерируемыми суточными отчётами
/// </summary>
public interface IAutoGeneratedDailyReportService
{
/// <summary>
/// Список файлов суточных отчётов
/// </summary>
/// <param name="idWell"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<PaginationContainer<AutoGeneratedDailyReportInfoDto>> GetListAsync(int idWell,
AutoGeneratedDailyReportRequest request,
CancellationToken cancellationToken);
/// <summary>
/// Генерация файла с отчётом
/// </summary>
/// <param name="idWell"></param>
/// <param name="reportDate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<(string fileName, Stream stream)> GenerateReportAsync(int idWell, DateOnly reportDate,
CancellationToken cancellationToken);
}

View File

@ -36,6 +36,7 @@
<EmbeddedResource Include="Services\WellOperationService\ScheduleReportTemplate.xlsx" /> <EmbeddedResource Include="Services\WellOperationService\ScheduleReportTemplate.xlsx" />
<EmbeddedResource Include="Services\WellOperationService\WellOperationImportTemplate.xlsx" /> <EmbeddedResource Include="Services\WellOperationService\WellOperationImportTemplate.xlsx" />
<EmbeddedResource Include="Services\ProcessMap\ProcessMapPlanTemplate.xlsx" /> <EmbeddedResource Include="Services\ProcessMap\ProcessMapPlanTemplate.xlsx" />
<EmbeddedResource Include="Services\AutoGeneratedDailyReports\AutogeneratedDailyReportTemplate.xlsx" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -23,7 +23,9 @@ using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using AsbCloudApp.Services.AutoGeneratedDailyReports;
using AsbCloudApp.Services.Notifications; using AsbCloudApp.Services.Notifications;
using AsbCloudInfrastructure.Services.AutoGeneratedDailyReports;
namespace AsbCloudInfrastructure namespace AsbCloudInfrastructure
{ {
@ -215,7 +217,10 @@ namespace AsbCloudInfrastructure
services.AddTransient<IWitsRecordRepository<AsbCloudApp.Data.WITS.Record8Dto>, WitsRecordRepository<AsbCloudApp.Data.WITS.Record8Dto, AsbCloudDb.Model.WITS.Record8>>(); services.AddTransient<IWitsRecordRepository<AsbCloudApp.Data.WITS.Record8Dto>, WitsRecordRepository<AsbCloudApp.Data.WITS.Record8Dto, AsbCloudDb.Model.WITS.Record8>>();
services.AddTransient<IWitsRecordRepository<AsbCloudApp.Data.WITS.Record50Dto>, WitsRecordRepository<AsbCloudApp.Data.WITS.Record50Dto, AsbCloudDb.Model.WITS.Record50>>(); services.AddTransient<IWitsRecordRepository<AsbCloudApp.Data.WITS.Record50Dto>, WitsRecordRepository<AsbCloudApp.Data.WITS.Record50Dto, AsbCloudDb.Model.WITS.Record50>>();
services.AddTransient<IWitsRecordRepository<AsbCloudApp.Data.WITS.Record60Dto>, WitsRecordRepository<AsbCloudApp.Data.WITS.Record60Dto, AsbCloudDb.Model.WITS.Record60>>(); services.AddTransient<IWitsRecordRepository<AsbCloudApp.Data.WITS.Record60Dto>, WitsRecordRepository<AsbCloudApp.Data.WITS.Record60Dto, AsbCloudDb.Model.WITS.Record60>>();
services.AddTransient<IWitsRecordRepository<AsbCloudApp.Data.WITS.Record61Dto>, WitsRecordRepository<AsbCloudApp.Data.WITS.Record61Dto, AsbCloudDb.Model.WITS.Record61>>(); services.AddTransient<IWitsRecordRepository<AsbCloudApp.Data.WITS.Record61Dto>, WitsRecordRepository<AsbCloudApp.Data.WITS.Record61Dto, AsbCloudDb.Model.WITS.Record61>>();
services.AddTransient<IAutoGeneratedDailyReportService, AutoGeneratedDailyReportService>();
services.AddTransient<IAutoGeneratedDailyReportMakerService, AutoGeneratedDailyReportMakerService>();
return services; return services;
} }

View File

@ -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<IExcelBlockWriter> blockWriters = new List<IExcelBlockWriter>()
{
new HeadExcelBlockWriter(),
new SubsystemExcelBlockWriter(),
new LimitingParameterExcelBlockWriter(),
new TimeBalanceExcelBlockWriter()
};
public async Task<Stream> 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<Stream> 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);
}
}
}

View File

@ -0,0 +1,248 @@
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<TelemetryDataSaubDto> telemetryDataCache;
private readonly ISubsystemOperationTimeService subsystemOperationTimeService;
private readonly ICrudRepository<SubsystemDto> subsystemRepository;
private readonly ILimitingParameterService limitingParameterService;
private readonly IAutoGeneratedDailyReportMakerService autoGeneratedDailyReportMakerService;
public AutoGeneratedDailyReportService(IWellService wellService,
IWellOperationRepository wellOperationRepository,
TelemetryDataCache<TelemetryDataSaubDto> telemetryDataCache,
ISubsystemOperationTimeService subsystemOperationTimeService,
ICrudRepository<SubsystemDto> 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<PaginationContainer<AutoGeneratedDailyReportInfoDto>> GetListAsync(int idWell,
AutoGeneratedDailyReportRequest request,
CancellationToken cancellationToken)
{
var result = new PaginationContainer<AutoGeneratedDailyReportInfoDto>
{
Skip = request.Skip ?? 0,
Take = request.Take ?? 10,
Items = Enumerable.Empty<AutoGeneratedDailyReportInfoDto>()
};
var reports = new List<AutoGeneratedDailyReportInfoDto>();
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;
result.Count = (int)(Math.Ceiling((datesRange.To - DateTime.UnixEpoch).TotalDays) - Math.Floor((datesRange.From - DateTime.UnixEpoch).TotalDays));
if (request.StartDate.HasValue)
{
var startDate = new DateTime(request.StartDate.Value.Year, request.StartDate.Value.Month,
request.StartDate.Value.Day);
if(startDate.Date >= datesRange.From.Date)
datesRange.From = startDate;
}
if (request.FinishDate.HasValue)
{
var finishDate = new DateTime(request.FinishDate.Value.Year, request.FinishDate.Value.Month,
request.FinishDate.Value.Day);
if (finishDate.Date <= datesRange.To.Date)
datesRange.To = finishDate;
}
for (int day = result.Skip; (day - result.Skip) < result.Take && (datesRange.From.AddDays(day)) <= datesRange.To; day++)
{
var dateFrom = datesRange.From.AddDays(day);
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;
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
{
Customer = customer?.Caption ?? string.Empty,
Deposit = well.Deposit ?? string.Empty,
Cluster = well.Cluster ?? string.Empty,
Well = well.Caption,
DepthFrom = factOperations.FirstOrDefault()?.DepthStart ?? 0.00,
DepthTo = factOperations.LastOrDefault()?.DepthEnd ?? 0.00
};
}
private async Task<IEnumerable<SubsystemRecordDto>> 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<IEnumerable<LimitingParameterRecordDto>> 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,
Hours = l.TotalMinutes,
PercentDepth = l.Depth / sumDepths,
Depth = l.Depth,
});
}
private Task<IEnumerable<WellOperationDto>> 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<IEnumerable<SubsystemStatDto>?> 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<IEnumerable<LimitingParameterDto>> 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 = 10240;
// TODO: Добавку размера сделать более предсказуемой на основе даты рапорта. что то типа `(Date.Ticks * idWell) % (fileSizeTemplate / 10)`
return new Random().Next(1, 8193) + fileSizeTemplate;
}
}

View File

@ -0,0 +1,34 @@
using AsbCloudApp.Data.AutogeneratedDailyReport;
using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports.AutogeneratedDailyReportBlocks;
public class HeadExcelBlockWriter : IExcelBlockWriter
{
private static readonly (int, int) customerCell = (2, 2);
private static readonly (int, int) depositCell = (4, 2);
private static readonly (int, int) clusterCell = (5, 2);
private static readonly (int, int) wellCell = (6, 2);
private const int dateRow = 9;
private const int dateFromColumn = 2;
private const int dateFromToColumn = 3;
private const int depthRow = 10;
private const int depthFromColumn = 2;
private const int depthToColumn = 3;
public void Write(IXLWorksheet sheet, AutoGeneratedDailyReportDto report)
{
sheet.Cell(customerCell.Item1, customerCell.Item2).Value = report.Head.Customer;
sheet.Cell(depositCell.Item1, depositCell.Item2).Value = report.Head.Deposit;
sheet.Cell(clusterCell.Item1, clusterCell.Item2).Value = report.Head.Cluster;
sheet.Cell(wellCell.Item1, wellCell.Item2).Value = report.Head.Well;
sheet.Cell(dateRow, dateFromColumn).Value = report.ReportDate;
sheet.Cell(dateRow, dateFromToColumn).Value = report.ReportDate.AddDays(1);
sheet.Cell(depthRow, depthFromColumn).Value = report.Head.DepthFrom;
sheet.Cell(depthRow, depthToColumn).Value = report.Head.DepthTo;
}
}

View File

@ -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);
}

View File

@ -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 = 20;
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].Hours;
row.Cell(columnPercentDepth).Value = report.LimitingParameters[i].PercentDepth;
}
}
}

View File

@ -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 = 13;
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;
}
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,89 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
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;
namespace AsbCloudWebApi.Controllers;
/// <summary>
/// Контроллер для авто-генерируемых суточных отчётов
/// </summary>
[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;
}
/// <summary>
/// Формирование отчёта
/// </summary>
/// <param name="idWell">Id скважины</param>
/// <param name="reportDate">Дата отчёта</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet]
[Route("generate")]
[ProducesResponseType(typeof(PhysicalFileResult), (int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> GenerateAsync([FromRoute] int idWell,
[Required] DateOnly reportDate,
CancellationToken cancellationToken)
{
if (!await CanUserAccessToWellAsync(idWell, cancellationToken))
return Forbid();
var reportFile = await autoGeneratedDailyReportService.GenerateReportAsync(idWell,
reportDate,
cancellationToken);
return File(reportFile.stream, "application/octet-stream", reportFile.fileName);
}
/// <summary>
/// Список файлов суточных отчётов
/// </summary>
/// <param name="idWell">Id скважины</param>
/// <param name="request">Параметры запроса</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(typeof(PaginationContainer<AutoGeneratedDailyReportInfoDto>), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetListAsync([FromRoute][Required] int idWell,
[FromQuery] AutoGeneratedDailyReportRequest request,
CancellationToken cancellationToken)
{
if (!await CanUserAccessToWellAsync(idWell, cancellationToken))
return Forbid();
var reports = await autoGeneratedDailyReportService.GetListAsync(idWell,
request,
cancellationToken);
return Ok(reports);
}
private async Task<bool> CanUserAccessToWellAsync(int idWell, CancellationToken cancellationToken)
{
int? idCompany = User.GetCompanyId();
return idCompany is not null && await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, cancellationToken).ConfigureAwait(false);
}
}