From bd5e4b0e714e111abed246952236761b9a732d59 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?= <da.stepanov@digitaldrilling.ru> Date: Thu, 20 Jul 2023 15:53:46 +0500 Subject: [PATCH 01/12] =?UTF-8?q?=D0=A4=D0=BE=D1=80=D0=BC=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B0=D0=B2=D1=82=D0=BE?= =?UTF-8?q?=D0=BC=D0=B0=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D1=85=20?= =?UTF-8?q?=D1=81=D1=83=D1=82=D0=BE=D1=87=D0=BD=D1=8B=D1=85=20=D0=BE=D1=82?= =?UTF-8?q?=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. Добавлены Dto. 2. Создан контроллер с заглушками. --- .../AutoGeneratedDailyReportDto.cs | 45 ++++++++++++++++ .../AutogeneratedDailyReport/HeadBlockDto.cs | 44 ++++++++++++++++ .../LimitingParameterRecordDto.cs | 22 ++++++++ .../SubsystemRecordDto.cs | 27 ++++++++++ .../AutoGeneratedDailyReportController.cs | 52 +++++++++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs create mode 100644 AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs create mode 100644 AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs create mode 100644 AsbCloudApp/Data/AutogeneratedDailyReport/SubsystemRecordDto.cs create mode 100644 AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs new file mode 100644 index 00000000..234a80c1 --- /dev/null +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; + +namespace AsbCloudApp.Data.AutogeneratedDailyReport; + +/// <summary> +/// DTO авто-сгенерированного суточного отчёта +/// </summary> +public class AutoGeneratedDailyReportDto +{ + /// <summary> + /// Дата формирования отчёта + /// </summary> + public DateOnly ReportDate { get; set; } + + /// <summary> + /// Название файла + /// </summary> + public string FileName { get; set; } = null!; + + /// <summary> + /// Начальная дата + /// </summary> + public DateOnly From { get; set; } + + /// <summary> + /// Конечная дата + /// </summary> + public DateOnly To { get; set; } + + /// <summary> + /// Блок заголовка + /// </summary> + public HeadBlockDto Head { get; set; } = null!; + + /// <summary> + /// Блок подсистем + /// </summary> + public IEnumerable<SubsystemRecordDto> Subsystems { get; set; } = null!; + + /// <summary> + /// Блок ограничивающих параметров + /// </summary> + public IEnumerable<LimitingParameterRecordDto> LimitingParameters { get; set; } = null!; +} \ No newline at end of file diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs new file mode 100644 index 00000000..756abba1 --- /dev/null +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs @@ -0,0 +1,44 @@ +using System; + +namespace AsbCloudApp.Data.AutogeneratedDailyReport; + +/// <summary> +/// ���� ��������� +/// </summary> +public class HeadBlockDto +{ + /// <summary> + /// �������� �������� + /// </summary> + public string WellName { get; set; } = null!; + + /// <summary> + /// �������� ����� + /// </summary> + public string ClusterName { get; set; } = null!; + + /// <summary> + /// �������� + /// </summary> + public string Customer { get; set; } = null!; + + /// <summary> + /// ������������� + /// </summary> + public string Deposit { get; set; } = null!; + + /// <summary> + /// ���� ������� + /// </summary> + public DateTime ReportDate { get; set; } + + /// <summary> + /// ������� ����� �� ���� ������ ��������� + /// </summary> + public double WellDepthIntervalStartDate { get; set; } + + /// <summary> + /// ������� ����� �� ���� ��������� ��������� + /// </summary> + public double WellDepthIntervalFinishDate { get; set; } +} \ No newline at end of file diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs new file mode 100644 index 00000000..67172684 --- /dev/null +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs @@ -0,0 +1,22 @@ +namespace AsbCloudApp.Data.AutogeneratedDailyReport; + +/// <summary> +/// Блок ограничивающих параметров +/// </summary> +public class LimitingParameterRecordDto +{ + /// <summary> + /// Время использования, мин + /// </summary> + public double TotalHours { get; set; } + + /// <summary> + /// Название ограничивающего параметра + /// </summary> + public string NameFeedRegulator { get; set; } = null!; + + /// <summary> + /// Процент по проходке, % + /// </summary> + public double PercentDepth { get; set; } +} \ No newline at end of file diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/SubsystemRecordDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/SubsystemRecordDto.cs new file mode 100644 index 00000000..34b2e4a7 --- /dev/null +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/SubsystemRecordDto.cs @@ -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; } +} \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs b/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs new file mode 100644 index 00000000..aeb8b9f7 --- /dev/null +++ b/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using AsbCloudApp.Data.AutogeneratedDailyReport; +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 +{ + /// <summary> + /// Метод генерации отчёта + /// </summary> + /// <param name="idWell">Id скважины</param> + /// <param name="date">Дата работы системы</param> + /// <param name="cancellationToken"></param> + /// <returns></returns> + [HttpPost] + [Route("generate")] + [ProducesResponseType(typeof(PhysicalFileResult), (int)HttpStatusCode.OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public Task<IActionResult> GenerateAsync([FromRoute] int idWell, DateOnly date, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + /// <summary> + /// Получение списка файлов отчёта + /// </summary> + /// <param name="idWell">Id скважины</param> + /// <param name="begin">Дата начала работ на скважине</param> + /// <param name="end">Дата окончания работ на скважине</param> + /// <param name="cancellationToken"></param> + /// <returns></returns> + [HttpGet] + [ProducesResponseType(typeof(IEnumerable<AutoGeneratedDailyReportDto>), (int)HttpStatusCode.OK)] + public Task<IActionResult> GetListAsync([FromRoute] int idWell, DateOnly? begin, DateOnly? end, + CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } +} \ No newline at end of file From 9429333b505a1c13608c2116a86173686dcca2d4 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?= <da.stepanov@digitaldrilling.ru> Date: Thu, 20 Jul 2023 15:56:12 +0500 Subject: [PATCH 02/12] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BA=D0=BE=D0=B4=D0=B8=D1=80=D0=BE=D0=B2=D0=BA?= =?UTF-8?q?=D1=83=20=D1=83=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AutogeneratedDailyReport/HeadBlockDto.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs index 756abba1..6ec65477 100644 --- a/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs @@ -3,42 +3,42 @@ using System; namespace AsbCloudApp.Data.AutogeneratedDailyReport; /// <summary> -/// ���� ��������� +/// Блок заголовка /// </summary> public class HeadBlockDto { /// <summary> - /// �������� �������� + /// Название скважины /// </summary> public string WellName { get; set; } = null!; /// <summary> - /// �������� ����� + /// Название куста /// </summary> public string ClusterName { get; set; } = null!; /// <summary> - /// �������� + /// Заказчик /// </summary> public string Customer { get; set; } = null!; /// <summary> - /// ������������� + /// Месторождение /// </summary> public string Deposit { get; set; } = null!; /// <summary> - /// ���� ������� + /// Дата создания отчёта /// </summary> public DateTime ReportDate { get; set; } /// <summary> - /// ������� ����� �� ���� ������ ��������� + /// Глубина забоя на дату начала интервала /// </summary> public double WellDepthIntervalStartDate { get; set; } /// <summary> - /// ������� ����� �� ���� ��������� ��������� + /// Глубина забоя на дату окончания интервала /// </summary> public double WellDepthIntervalFinishDate { get; set; } } \ No newline at end of file From d0d8d48c4e59b377deca3dd2183f0fe440f55737 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?= <da.stepanov@digitaldrilling.ru> Date: Fri, 21 Jul 2023 09:07:58 +0500 Subject: [PATCH 03/12] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B2=20Dto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AutoGeneratedDailyReportDto.cs | 14 +++++++------- .../AutogeneratedDailyReport/HeadBlockDto.cs | 11 ++++++++--- .../TimeBalanceRecordDto.cs | 17 +++++++++++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 AsbCloudApp/Data/AutogeneratedDailyReport/TimeBalanceRecordDto.cs diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs index 234a80c1..d24be197 100644 --- a/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs @@ -19,14 +19,9 @@ public class AutoGeneratedDailyReportDto public string FileName { get; set; } = null!; /// <summary> - /// Начальная дата + /// Размер файла /// </summary> - public DateOnly From { get; set; } - - /// <summary> - /// Конечная дата - /// </summary> - public DateOnly To { get; set; } + public int FileSize { get; set; } /// <summary> /// Блок заголовка @@ -42,4 +37,9 @@ public class AutoGeneratedDailyReportDto /// Блок ограничивающих параметров /// </summary> public IEnumerable<LimitingParameterRecordDto> LimitingParameters { get; set; } = null!; + + /// <summary> + /// Баланс времени + /// </summary> + public IEnumerable<TimeBalanceRecordDto> TimeBalance { get; set; } = null!; } \ No newline at end of file diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs index 6ec65477..125cd80a 100644 --- a/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs @@ -28,10 +28,15 @@ public class HeadBlockDto public string Deposit { get; set; } = null!; /// <summary> - /// Дата создания отчёта + /// Начальная дата /// </summary> - public DateTime ReportDate { get; set; } - + public DateOnly From { get; set; } + + /// <summary> + /// Конечная дата + /// </summary> + public DateOnly To => From.AddDays(1); + /// <summary> /// Глубина забоя на дату начала интервала /// </summary> diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/TimeBalanceRecordDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/TimeBalanceRecordDto.cs new file mode 100644 index 00000000..a6ac9845 --- /dev/null +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/TimeBalanceRecordDto.cs @@ -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; } +} \ No newline at end of file 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?= <da.stepanov@digitaldrilling.ru> Date: Mon, 24 Jul 2023 11:14:07 +0500 Subject: [PATCH 04/12] =?UTF-8?q?=D0=A4=D0=BE=D1=80=D0=BC=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=81=D1=83=D1=82=D0=BE?= =?UTF-8?q?=D1=87=D0=BD=D1=8B=D1=85=20=D0=BE=D1=82=D1=87=D1=91=D1=82=D0=BE?= =?UTF-8?q?=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 /// <summary> /// Блок подсистем /// </summary> - public IEnumerable<SubsystemRecordDto> Subsystems { get; set; } = null!; + public SubsystemRecordDto[] Subsystems { get; set; } = null!; /// <summary> /// Блок ограничивающих параметров /// </summary> - public IEnumerable<LimitingParameterRecordDto> LimitingParameters { get; set; } = null!; + public LimitingParameterRecordDto[] LimitingParameters { get; set; } = null!; /// <summary> /// Баланс времени /// </summary> - public IEnumerable<TimeBalanceRecordDto> 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 /// </summary> public double TotalHours { get; set; } + /// <summary> + /// Проходка + /// </summary> + public double Depth { get; set; } + /// <summary> /// Название ограничивающего параметра /// </summary> 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; + +/// <summary> +/// Параметры запроса для получения авто-генерируемых суточных отчётов +/// </summary> +public class AutoGeneratedDailyReportRequest : RequestBase +{ + /// <summary> + /// Дата начала периода + /// </summary> + public DateOnly? StartDate { get; set; } + + /// <summary> + /// Дата конца периода + /// </summary> + 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; + +/// <summary> +/// Сервис для генерации файлов авто-генерируемых суточный отчётов +/// </summary> +public interface IAutoGeneratedDailyReportMakerService +{ + /// <summary> + /// Генерация файла + /// </summary> + /// <param name="report"></param> + /// <param name="cancellationToken"></param> + /// <returns></returns> + Task<Stream> 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; + +/// <summary> +/// Сервис для работы с авто-генерируемыми суточными отчётами +/// </summary> +public interface IAutoGeneratedDailyReportService +{ + /// <summary> + /// Список файлов суточных отчётов + /// </summary> + /// <param name="idWell"></param> + /// <param name="request"></param> + /// <param name="cancellationToken"></param> + /// <returns></returns> + Task<PaginationContainer<AutoGeneratedDailyReportDto>> 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); +} \ 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 @@ <EmbeddedResource Include="Services\WellOperationService\ScheduleReportTemplate.xlsx" /> <EmbeddedResource Include="Services\WellOperationService\WellOperationImportTemplate.xlsx" /> <EmbeddedResource Include="Services\ProcessMap\ProcessMapPlanTemplate.xlsx" /> + <EmbeddedResource Include="Services\AutoGeneratedDailyReports\AutogeneratedDailyReportTemplate.xlsx" /> </ItemGroup> <ItemGroup> 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<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.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; } 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<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); + } + } +} \ 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<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<AutoGeneratedDailyReportDto>> GetListAsync(int idWell, + AutoGeneratedDailyReportRequest request, + CancellationToken cancellationToken) + { + var result = new PaginationContainer<AutoGeneratedDailyReportDto> + { + Skip = request.Skip ?? 0, + Take = request.Take ?? 10, + }; + + var reports = new List<AutoGeneratedDailyReportDto>(); + + 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<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, + TotalHours = 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 = 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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 Du+$&{ literal 0 HcmV?d00001 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; /// <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="date">Дата работы системы</param> + /// <param name="reportDate">Дата отчёта</param> /// <param name="cancellationToken"></param> /// <returns></returns> [HttpPost] [Route("generate")] [ProducesResponseType(typeof(PhysicalFileResult), (int)HttpStatusCode.OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public Task<IActionResult> GenerateAsync([FromRoute] int idWell, DateOnly date, CancellationToken cancellationToken) + public async Task<IActionResult> 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); } /// <summary> - /// Получение списка файлов отчёта + /// Список файлов суточных отчётов /// </summary> /// <param name="idWell">Id скважины</param> - /// <param name="begin">Дата начала работ на скважине</param> - /// <param name="end">Дата окончания работ на скважине</param> + /// <param name="request">Параметры запроса</param> /// <param name="cancellationToken"></param> /// <returns></returns> [HttpGet] [ProducesResponseType(typeof(IEnumerable<AutoGeneratedDailyReportDto>), (int)HttpStatusCode.OK)] - public Task<IActionResult> GetListAsync([FromRoute] int idWell, DateOnly? begin, DateOnly? end, + public async Task<IActionResult> 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<bool> 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 From 65ef2242e89f728501e31eb81be2d62156518e7b 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?= <da.stepanov@digitaldrilling.ru> Date: Mon, 24 Jul 2023 16:00:40 +0500 Subject: [PATCH 05/12] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80?= =?UTF-8?q?=D1=82=D0=B0/=D1=8D=D0=BA=D1=81=D0=BF=D0=BE=D1=80=D1=82=D0=B0?= =?UTF-8?q?=20=D0=A0=D0=A2=D0=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Добавил флаг, который определяет следует ли удалить РТК у скважины если импортируемый файл валидный. 2. Поправил пути в методах экспорта/импорта в контроллере РТК. --- .../Repositories/IProcessMapRepository.cs | 17 +++++++++++++---- .../Services/IProcessMapPlanImportService.cs | 4 +++- .../Repository/ProcessMapRepository.cs | 10 ++++++++++ .../ProcessMap/ProcessMapPlanImportService.cs | 6 +++++- .../Controllers/ProcessMapController.cs | 9 ++++++--- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/AsbCloudApp/Repositories/IProcessMapRepository.cs b/AsbCloudApp/Repositories/IProcessMapRepository.cs index b126e18a..10710d9a 100644 --- a/AsbCloudApp/Repositories/IProcessMapRepository.cs +++ b/AsbCloudApp/Repositories/IProcessMapRepository.cs @@ -9,12 +9,12 @@ using System.Threading.Tasks; namespace AsbCloudApp.Repositories { /// <summary> - /// ��� + /// РТК /// </summary> public interface IProcessMapPlanRepository : IRepositoryWellRelated<ProcessMapPlanDto> { /// <summary> - /// �������� ��������� ������� ������� � ����. + /// Получить параметры бурения начиная с даты. /// </summary> /// <param name="idWell"></param> /// <param name="updateFrom"></param> @@ -24,11 +24,20 @@ namespace AsbCloudApp.Repositories DateTime? updateFrom, CancellationToken token = default); /// <summary> - /// �������� ��������� ������� + /// Получить параметры бурения /// </summary> /// <param name="requests"></param> /// <param name="token"></param> /// <returns></returns> - Task<IEnumerable<ProcessMapPlanDto>> GetProcessMapAsync(IEnumerable<ProcessMapRequest> requests, CancellationToken token); + Task<IEnumerable<ProcessMapPlanDto>> GetProcessMapAsync(IEnumerable<ProcessMapRequest> requests, + CancellationToken token); + + /// <summary> + /// Удалить РТК + /// </summary> + /// <param name="idWell"></param> + /// <param name="cancellationToken"></param> + /// <returns></returns> + Task<int> RemoveRangeAsync(int idWell, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/AsbCloudApp/Services/IProcessMapPlanImportService.cs b/AsbCloudApp/Services/IProcessMapPlanImportService.cs index 84442df1..c5daa587 100644 --- a/AsbCloudApp/Services/IProcessMapPlanImportService.cs +++ b/AsbCloudApp/Services/IProcessMapPlanImportService.cs @@ -14,10 +14,12 @@ public interface IProcessMapPlanImportService /// </summary> /// <param name="idWell"></param> /// <param name="idUser"></param> + /// <param name="deleteProcessMapPlansBeforeImport"></param> /// <param name="stream"></param> /// <param name="cancellationToken"></param> /// <returns></returns> - Task ImportAsync(int idWell, int idUser, Stream stream, CancellationToken cancellationToken); + Task ImportAsync(int idWell, int idUser, bool deleteProcessMapPlansBeforeImport, Stream stream, + CancellationToken cancellationToken); /// <summary> /// Сформировать файл с данными diff --git a/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs b/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs index b80fe6d9..1d3fec14 100644 --- a/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs +++ b/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs @@ -60,6 +60,16 @@ namespace AsbCloudInfrastructure.Repository return dtos; } + public async Task<int> RemoveRangeAsync(int idWell, CancellationToken cancellationToken) + { + var entitites = await dbContext.ProcessMap.Where(x => x.IdWell == idWell) + .ToListAsync(cancellationToken); + + dbContext.ProcessMap.RemoveRange(entitites); + + return await dbContext.SaveChangesAsync(cancellationToken); + } + public override async Task<int> InsertAsync(ProcessMapPlanDto dto, CancellationToken token) { diff --git a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs index 60fbc9bd..06551f24 100644 --- a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs +++ b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs @@ -52,7 +52,8 @@ public class ProcessMapPlanImportService : IProcessMapPlanImportService this.wellSectionTypeRepository = wellSectionTypeRepository; } - public async Task ImportAsync(int idWell, int idUser, Stream stream, CancellationToken cancellationToken) + public async Task ImportAsync(int idWell, int idUser, bool deleteProcessMapPlansBeforeImport, Stream stream, + CancellationToken cancellationToken) { sections = (await wellSectionTypeRepository.GetAllAsync(cancellationToken)).ToArray(); @@ -60,6 +61,9 @@ public class ProcessMapPlanImportService : IProcessMapPlanImportService var processPlanMaps = ParseWorkBook(workBook); + if (deleteProcessMapPlansBeforeImport) + await processMapPlanRepository.RemoveRangeAsync(idWell, cancellationToken); + foreach (var processPlanMap in processPlanMaps) { processPlanMap.IdWell = idWell; diff --git a/AsbCloudWebApi/Controllers/ProcessMapController.cs b/AsbCloudWebApi/Controllers/ProcessMapController.cs index 474c260c..95f41405 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapController.cs @@ -183,12 +183,14 @@ namespace AsbCloudWebApi.Controllers /// Импортирует плановой РТК из excel (xlsx) файла /// </summary> /// <param name="idWell">Id скважины</param> + /// <param name="options">Удалить РТК перед импортом = 1, если файл валидный</param> /// <param name="file">Загружаемый файл</param> /// <param name="cancellationToken"></param> /// <returns></returns> [HttpPost] - [Route("import")] + [Route("import/{idWell}/{options}")] public async Task<IActionResult> ImportAsync(int idWell, + int options, [Required] IFormFile file, CancellationToken cancellationToken) { @@ -204,6 +206,7 @@ namespace AsbCloudWebApi.Controllers await processMapPlanImportService.ImportAsync(idWell, idUser.Value, + (options & 1) > 0, stream, cancellationToken); @@ -217,9 +220,9 @@ namespace AsbCloudWebApi.Controllers /// <param name="cancellationToken"></param> /// <returns></returns> [HttpGet] - [Route("export")] + [Route("export/{idWell}")] [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)] - public async Task<IActionResult> ExportAsync([FromQuery] int idWell, CancellationToken cancellationToken) + public async Task<IActionResult> ExportAsync(int idWell, CancellationToken cancellationToken) { int? idUser = User.GetUserId(); From 1a33cfb0ad2d43d30403140401a783e70187be77 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?= <da.stepanov@digitaldrilling.ru> Date: Mon, 24 Jul 2023 16:27:31 +0500 Subject: [PATCH 06/12] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D1=88=D0=B0?= =?UTF-8?q?=D0=B1=D0=BB=D0=BE=D0=BD=D0=B0=20+=20=D0=BC=D0=B5=D0=BB=D0=BA?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=84=D0=B8=D0=BA=D1=81=D1=8B=20=D0=B2=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=82=D1=80=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HeadExcelBlockWriter.cs | 41 ++++++++---------- .../LimitingParameterExcelBlockWriter.cs | 2 +- .../SubsystemExcelBlockWriter.cs | 2 +- .../AutogeneratedDailyReportTemplate.xlsx | Bin 9272 -> 10158 bytes .../AutoGeneratedDailyReportController.cs | 6 +-- 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs index a81f7c08..b3ef2fa5 100644 --- a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs @@ -5,33 +5,30 @@ namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports.Autogenerate 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 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 columnFrom = 1; - private const int columnTo = 2; - private const int columnWellDepthIntervalStartDate = 3; - private const int columnWellDepthIntervalFinishDate = 4; + 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) { - const int rowHeaderBlockSectionOne = 2; - const int rowHeaderBlockSectionTwo = 5; - - var rowSectionOne = sheet.Row(1 + rowHeaderBlockSectionOne); + 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.ClusterName; + sheet.Cell(wellCell.Item1, wellCell.Item2).Value = report.Head.WellName; - 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; + sheet.Cell(dateRow, dateFromColumn).Value = report.Head.From; + sheet.Cell(dateRow, dateFromToColumn).Value = report.Head.To; - 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; + sheet.Cell(depthRow, depthFromColumn).Value = report.Head.WellDepthIntervalStartDate; + sheet.Cell(depthRow, depthToColumn).Value = report.Head.WellDepthIntervalFinishDate; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs index b2edb32c..3fd7b311 100644 --- a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs @@ -6,7 +6,7 @@ namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports.Autogenerate public class LimitingParameterExcelBlockWriter : IExcelBlockWriter { - private const int rowHeaderBlock = 18; + private const int rowHeaderBlock = 20; private const int columnNameFeedRegulator = 1; private const int columnDepth = 2; diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/SubsystemExcelBlockWriter.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/SubsystemExcelBlockWriter.cs index c1e8182c..ec32c60e 100644 --- a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/SubsystemExcelBlockWriter.cs +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/SubsystemExcelBlockWriter.cs @@ -6,7 +6,7 @@ namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports.Autogenerate public class SubsystemExcelBlockWriter : IExcelBlockWriter { - private const int rowHeaderBlock = 10; + private const int rowHeaderBlock = 13; private const int columnName = 1; private const int columnKUsage = 2; diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportTemplate.xlsx b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportTemplate.xlsx index 55c2ae12c2f940d501deb62181e78e8a3c4bd120..0a887a4ea3cc1fc8e1837d15eef29dbde554180c 100644 GIT binary patch delta 6751 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 NOHYgZhT)&t{{j`dfI9#H delta 5880 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 diff --git a/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs b/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs index 08e6dc9c..7c090aa7 100644 --- a/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs +++ b/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs @@ -1,9 +1,9 @@ using System; -using System.Collections.Generic; 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; @@ -39,7 +39,7 @@ public class AutoGeneratedDailyReportController : ControllerBase /// <param name="reportDate">Дата отчёта</param> /// <param name="cancellationToken"></param> /// <returns></returns> - [HttpPost] + [HttpGet] [Route("generate")] [ProducesResponseType(typeof(PhysicalFileResult), (int)HttpStatusCode.OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] @@ -65,7 +65,7 @@ public class AutoGeneratedDailyReportController : ControllerBase /// <param name="cancellationToken"></param> /// <returns></returns> [HttpGet] - [ProducesResponseType(typeof(IEnumerable<AutoGeneratedDailyReportDto>), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(PaginationContainer<AutoGeneratedDailyReportDto>), (int)HttpStatusCode.OK)] public async Task<IActionResult> GetListAsync([FromRoute][Required] int idWell, [FromQuery] AutoGeneratedDailyReportRequest request, CancellationToken cancellationToken) From e3a67192fa8d65069dd0e19ce69d74bc7032bf24 Mon Sep 17 00:00:00 2001 From: Olga Nemt <on.nemtina@digitaldrilling.ru> Date: Tue, 25 Jul 2023 12:33:25 +0500 Subject: [PATCH 07/12] =?UTF-8?q?WellSectionTypeDto:=20=D0=B8=D0=B7=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D0=BD=20=D1=82=D0=B8=D0=BF=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=80=D1=8F=D0=B4=D0=BA=D0=B0,=20=D1=81=20int=20=D0=BD=D0=B0?= =?UTF-8?q?=20float?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Data/WellSectionTypeDto.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsbCloudApp/Data/WellSectionTypeDto.cs b/AsbCloudApp/Data/WellSectionTypeDto.cs index 88434e20..28e9ba68 100644 --- a/AsbCloudApp/Data/WellSectionTypeDto.cs +++ b/AsbCloudApp/Data/WellSectionTypeDto.cs @@ -18,5 +18,5 @@ public class WellSectionTypeDto : IId /// <summary> /// Порядок /// </summary> - public int Order { get; set; } + public float Order { get; set; } } \ No newline at end of file From 7a64056b49a3d7cb7433786a2f4d59c3b7836692 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?= <da.stepanov@digitaldrilling.ru> Date: Tue, 25 Jul 2023 13:43:12 +0500 Subject: [PATCH 08/12] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D0=BE=D0=B4=D0=B0=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BF=D0=BB=D0=B0=D0=BD=D0=BE=D0=B2=D1=8B=D1=85?= =?UTF-8?q?=20=D0=A0=D0=A2=D0=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repository/ProcessMapRepository.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs b/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs index 1d3fec14..43d11179 100644 --- a/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs +++ b/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs @@ -60,14 +60,13 @@ namespace AsbCloudInfrastructure.Repository return dtos; } - public async Task<int> RemoveRangeAsync(int idWell, CancellationToken cancellationToken) + public Task<int> RemoveRangeAsync(int idWell, CancellationToken cancellationToken) { - var entitites = await dbContext.ProcessMap.Where(x => x.IdWell == idWell) - .ToListAsync(cancellationToken); - + var entitites = dbContext.ProcessMap.Where(x => x.IdWell == idWell); + dbContext.ProcessMap.RemoveRange(entitites); - return await dbContext.SaveChangesAsync(cancellationToken); + return dbContext.SaveChangesAsync(cancellationToken); } public override async Task<int> InsertAsync(ProcessMapPlanDto dto, From 7ba021c9e71cd59ead81207dbf0d6784aff9a703 Mon Sep 17 00:00:00 2001 From: ngfrolov <ng.frolov@autodrilling.ru> Date: Tue, 25 Jul 2023 16:40:25 +0500 Subject: [PATCH 09/12] =?UTF-8?q?IProcessMapPlanRepository=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=B4=D0=BE=D0=BA=D1=83?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8E,=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B8=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Repositories/IProcessMapRepository.cs | 10 +++++----- .../Repository/ProcessMapRepository.cs | 6 +++--- .../Services/ProcessMap/ProcessMapPlanImportService.cs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/AsbCloudApp/Repositories/IProcessMapRepository.cs b/AsbCloudApp/Repositories/IProcessMapRepository.cs index 10710d9a..f6dbe6b4 100644 --- a/AsbCloudApp/Repositories/IProcessMapRepository.cs +++ b/AsbCloudApp/Repositories/IProcessMapRepository.cs @@ -9,12 +9,12 @@ using System.Threading.Tasks; namespace AsbCloudApp.Repositories { /// <summary> - /// РТК + /// РТК-план /// </summary> public interface IProcessMapPlanRepository : IRepositoryWellRelated<ProcessMapPlanDto> { /// <summary> - /// Получить параметры бурения начиная с даты. + /// Получить РТК-план начиная с даты. /// </summary> /// <param name="idWell"></param> /// <param name="updateFrom"></param> @@ -24,7 +24,7 @@ namespace AsbCloudApp.Repositories DateTime? updateFrom, CancellationToken token = default); /// <summary> - /// Получить параметры бурения + /// Получить РТК-план /// </summary> /// <param name="requests"></param> /// <param name="token"></param> @@ -33,11 +33,11 @@ namespace AsbCloudApp.Repositories CancellationToken token); /// <summary> - /// Удалить РТК + /// Удалить РТК-план по скважине /// </summary> /// <param name="idWell"></param> /// <param name="cancellationToken"></param> /// <returns></returns> - Task<int> RemoveRangeAsync(int idWell, CancellationToken cancellationToken); + Task<int> RemoveByWellAsync(int idWell, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs b/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs index 43d11179..6597210b 100644 --- a/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs +++ b/AsbCloudInfrastructure/Repository/ProcessMapRepository.cs @@ -60,11 +60,11 @@ namespace AsbCloudInfrastructure.Repository return dtos; } - public Task<int> RemoveRangeAsync(int idWell, CancellationToken cancellationToken) + public Task<int> RemoveByWellAsync(int idWell, CancellationToken cancellationToken) { - var entitites = dbContext.ProcessMap.Where(x => x.IdWell == idWell); + var query = dbContext.ProcessMap.Where(x => x.IdWell == idWell); - dbContext.ProcessMap.RemoveRange(entitites); + dbContext.ProcessMap.RemoveRange(query); return dbContext.SaveChangesAsync(cancellationToken); } diff --git a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs index 06551f24..2b4467af 100644 --- a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs +++ b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs @@ -62,7 +62,7 @@ public class ProcessMapPlanImportService : IProcessMapPlanImportService var processPlanMaps = ParseWorkBook(workBook); if (deleteProcessMapPlansBeforeImport) - await processMapPlanRepository.RemoveRangeAsync(idWell, cancellationToken); + await processMapPlanRepository.RemoveByWellAsync(idWell, cancellationToken); foreach (var processPlanMap in processPlanMaps) { From b7f5739d8cc20d898d6485f616d298a9ef0088b8 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?= <da.stepanov@digitaldrilling.ru> Date: Tue, 25 Jul 2023 17:07:50 +0500 Subject: [PATCH 10/12] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AutoGeneratedDailyReportDto.cs | 19 +-------- .../AutoGeneratedDailyReportInfoDto.cs | 25 ++++++++++++ .../AutogeneratedDailyReport/HeadBlockDto.cs | 20 ++-------- .../LimitingParameterRecordDto.cs | 4 +- .../IAutoGeneratedDailyReportService.cs | 2 +- .../AutoGeneratedDailyReportService.cs | 39 +++++++++++-------- .../HeadExcelBlockWriter.cs | 12 +++--- .../LimitingParameterExcelBlockWriter.cs | 2 +- .../AutoGeneratedDailyReportController.cs | 4 +- 9 files changed, 64 insertions(+), 63 deletions(-) create mode 100644 AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportInfoDto.cs diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs index cad747c8..2d54b453 100644 --- a/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs @@ -1,27 +1,10 @@ -using System; - namespace AsbCloudApp.Data.AutogeneratedDailyReport; /// <summary> /// DTO авто-сгенерированного суточного отчёта /// </summary> -public class AutoGeneratedDailyReportDto +public class AutoGeneratedDailyReportDto : AutoGeneratedDailyReportInfoDto { - /// <summary> - /// Дата формирования отчёта - /// </summary> - public DateOnly ReportDate { get; set; } - - /// <summary> - /// Название файла - /// </summary> - public string FileName { get; set; } = null!; - - /// <summary> - /// Размер файла - /// </summary> - public int FileSize { get; set; } - /// <summary> /// Блок заголовка /// </summary> diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportInfoDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportInfoDto.cs new file mode 100644 index 00000000..0034879a --- /dev/null +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportInfoDto.cs @@ -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; } + +} \ No newline at end of file diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs index 125cd80a..43ff5246 100644 --- a/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/HeadBlockDto.cs @@ -1,5 +1,3 @@ -using System; - namespace AsbCloudApp.Data.AutogeneratedDailyReport; /// <summary> @@ -10,12 +8,12 @@ public class HeadBlockDto /// <summary> /// Название скважины /// </summary> - public string WellName { get; set; } = null!; + public string Well { get; set; } = null!; /// <summary> /// Название куста /// </summary> - public string ClusterName { get; set; } = null!; + public string Cluster { get; set; } = null!; /// <summary> /// Заказчик @@ -26,24 +24,14 @@ public class HeadBlockDto /// Месторождение /// </summary> public string Deposit { get; set; } = null!; - - /// <summary> - /// Начальная дата - /// </summary> - public DateOnly From { get; set; } - - /// <summary> - /// Конечная дата - /// </summary> - public DateOnly To => From.AddDays(1); /// <summary> /// Глубина забоя на дату начала интервала /// </summary> - public double WellDepthIntervalStartDate { get; set; } + public double DepthFrom { get; set; } /// <summary> /// Глубина забоя на дату окончания интервала /// </summary> - public double WellDepthIntervalFinishDate { get; set; } + public double DepthTo { get; set; } } \ No newline at end of file diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs index 7dc87b05..e62b32d9 100644 --- a/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/LimitingParameterRecordDto.cs @@ -8,10 +8,10 @@ public class LimitingParameterRecordDto /// <summary> /// Время использования, мин /// </summary> - public double TotalHours { get; set; } + public double Hours { get; set; } /// <summary> - /// Проходка + /// Проходка, м /// </summary> public double Depth { get; set; } diff --git a/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs b/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs index b5a4d5ee..16cdaed6 100644 --- a/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs +++ b/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs @@ -20,7 +20,7 @@ public interface IAutoGeneratedDailyReportService /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> - Task<PaginationContainer<AutoGeneratedDailyReportDto>> GetListAsync(int idWell, + Task<PaginationContainer<AutoGeneratedDailyReportInfoDto>> GetListAsync(int idWell, AutoGeneratedDailyReportRequest request, CancellationToken cancellationToken); diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs index 89f94c43..ba50a6d4 100644 --- a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs @@ -48,17 +48,18 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService this.autoGeneratedDailyReportMakerService = autoGeneratedDailyReportMakerService; } - public async Task<PaginationContainer<AutoGeneratedDailyReportDto>> GetListAsync(int idWell, + public async Task<PaginationContainer<AutoGeneratedDailyReportInfoDto>> GetListAsync(int idWell, AutoGeneratedDailyReportRequest request, CancellationToken cancellationToken) { - var result = new PaginationContainer<AutoGeneratedDailyReportDto> + var result = new PaginationContainer<AutoGeneratedDailyReportInfoDto> { Skip = request.Skip ?? 0, Take = request.Take ?? 10, + Items = Enumerable.Empty<AutoGeneratedDailyReportInfoDto>() }; - var reports = new List<AutoGeneratedDailyReportDto>(); + var reports = new List<AutoGeneratedDailyReportInfoDto>(); var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken) ?? throw new ArgumentInvalidException("Скважина не найдена", nameof(idWell)); @@ -75,8 +76,9 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService { var startDate = new DateTime(request.StartDate.Value.Year, request.StartDate.Value.Month, request.StartDate.Value.Day); - - datesRange.From = startDate; + + if(startDate.Date >= datesRange.From.Date) + datesRange.From = startDate; } if (request.FinishDate.HasValue) @@ -84,11 +86,14 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService var finishDate = new DateTime(request.FinishDate.Value.Year, request.FinishDate.Value.Month, request.FinishDate.Value.Day); - datesRange.To = finishDate; + if (finishDate.Date <= datesRange.To.Date) + datesRange.To = finishDate; } - - for (var dateFrom = datesRange.From; dateFrom <= datesRange.To; dateFrom = dateFrom.AddDays(1)) + + 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)), @@ -96,9 +101,10 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService FileSize = GetFileSize() / 1024, }); } - - result.Items = reports.Skip(result.Skip).Take(result.Take); + result.Items = reports; + result.Count = reports.Count; + return result; } @@ -143,13 +149,12 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService 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 + Cluster = well.Cluster ?? string.Empty, + Well = well.Caption, + DepthFrom = factOperations.FirstOrDefault()?.DepthStart ?? 0.00, + DepthTo = factOperations.LastOrDefault()?.DepthEnd ?? 0.00 }; } @@ -186,7 +191,7 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService return limitingParameterStats.Select(l => new LimitingParameterRecordDto { NameFeedRegulator = l.NameFeedRegulator, - TotalHours = l.TotalMinutes, + Hours = l.TotalMinutes, PercentDepth = l.Depth / sumDepths, Depth = l.Depth, }); @@ -235,7 +240,7 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService private int GetFileSize() { - const int fileSizeTemplate = 8192; + const int fileSizeTemplate = 10240; return new Random().Next(1, 8193) + fileSizeTemplate; } diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs index b3ef2fa5..dad6bf35 100644 --- a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/HeadExcelBlockWriter.cs @@ -22,13 +22,13 @@ public class HeadExcelBlockWriter : IExcelBlockWriter { 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.ClusterName; - sheet.Cell(wellCell.Item1, wellCell.Item2).Value = report.Head.WellName; + 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.Head.From; - sheet.Cell(dateRow, dateFromToColumn).Value = report.Head.To; + sheet.Cell(dateRow, dateFromColumn).Value = report.ReportDate; + sheet.Cell(dateRow, dateFromToColumn).Value = report.ReportDate.AddDays(1); - sheet.Cell(depthRow, depthFromColumn).Value = report.Head.WellDepthIntervalStartDate; - sheet.Cell(depthRow, depthToColumn).Value = report.Head.WellDepthIntervalFinishDate; + sheet.Cell(depthRow, depthFromColumn).Value = report.Head.DepthFrom; + sheet.Cell(depthRow, depthToColumn).Value = report.Head.DepthTo; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs index 3fd7b311..8e99e755 100644 --- a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutogeneratedDailyReportBlocks/LimitingParameterExcelBlockWriter.cs @@ -24,7 +24,7 @@ public class LimitingParameterExcelBlockWriter : IExcelBlockWriter 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(columnTotalHours).Value = report.LimitingParameters[i].Hours; row.Cell(columnPercentDepth).Value = report.LimitingParameters[i].PercentDepth; } } diff --git a/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs b/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs index 7c090aa7..0ef6c6da 100644 --- a/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs +++ b/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs @@ -65,8 +65,8 @@ public class AutoGeneratedDailyReportController : ControllerBase /// <param name="cancellationToken"></param> /// <returns></returns> [HttpGet] - [ProducesResponseType(typeof(PaginationContainer<AutoGeneratedDailyReportDto>), (int)HttpStatusCode.OK)] - public async Task<IActionResult> GetListAsync([FromRoute][Required] int idWell, + [ProducesResponseType(typeof(PaginationContainer<AutoGeneratedDailyReportInfoDto>), (int)HttpStatusCode.OK)] + public async Task<IActionResult> GetListAsync([FromRoute][Required] int idWell, [FromQuery] AutoGeneratedDailyReportRequest request, CancellationToken cancellationToken) { From 39dcf498125cbe5afdf65164dba602353857cbbc Mon Sep 17 00:00:00 2001 From: ngfrolov <ng.frolov@autodrilling.ru> Date: Tue, 25 Jul 2023 17:31:54 +0500 Subject: [PATCH 11/12] AutoGeneratedDailyReportService.GetListAsync(..) fix Count calculations. --- .../AutoGeneratedDailyReportService.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs index ba50a6d4..80aa5e1c 100644 --- a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs @@ -69,12 +69,14 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService var datesRange = telemetryDataCache.GetOrDefaultDataDateRange(well.IdTelemetry.Value); - if (datesRange is null) + 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, + var startDate = new DateTime(request.StartDate.Value.Year, request.StartDate.Value.Month, request.StartDate.Value.Day); if(startDate.Date >= datesRange.From.Date) @@ -103,7 +105,6 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService } result.Items = reports; - result.Count = reports.Count; return result; } From 7e750ed45544df709695863e21f7b8f98327c474 Mon Sep 17 00:00:00 2001 From: ngfrolov <ng.frolov@autodrilling.ru> Date: Tue, 25 Jul 2023 17:38:58 +0500 Subject: [PATCH 12/12] =?UTF-8?q?AutoGeneratedDailyReport=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs | 1 + .../AutoGeneratedDailyReportService.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs index 2d54b453..ea3b3c53 100644 --- a/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs +++ b/AsbCloudApp/Data/AutogeneratedDailyReport/AutoGeneratedDailyReportDto.cs @@ -10,6 +10,7 @@ public class AutoGeneratedDailyReportDto : AutoGeneratedDailyReportInfoDto /// </summary> public HeadBlockDto Head { get; set; } = null!; + //TODO: поля не должны быть массивами /// <summary> /// Блок подсистем /// </summary> diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs index 80aa5e1c..4012ebdf 100644 --- a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs +++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs @@ -242,7 +242,7 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService private int GetFileSize() { const int fileSizeTemplate = 10240; - - return new Random().Next(1, 8193) + fileSizeTemplate; + // TODO: Добавку размера сделать более предсказуемой на основе даты рапорта. что то типа `(Date.Ticks * idWell) % (fileSizeTemplate / 10)` + return new Random().Next(1, 8193) + fileSizeTemplate; } } \ No newline at end of file