From d4417a555289445a59059261e99bbf54e9d67f31 Mon Sep 17 00:00:00 2001 From: Olga Nemt Date: Mon, 2 Oct 2023 17:53:34 +0500 Subject: [PATCH 01/11] =?UTF-8?q?=D0=90=D0=BD=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BA=D0=B0=20=D0=BF=D0=BE=20=D1=83=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=B0=D0=BD=D0=B8=D1=8E=20=D0=B2=20=D0=BA=D0=BB=D0=B8?= =?UTF-8?q?=D0=BD=D1=8C=D1=8F=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Data/SlipsStatDto.cs | 44 +++++ .../Repositories/ISlipsStatsRepository.cs | 23 +++ AsbCloudApp/Requests/OperationStatRequest.cs | 36 ++++ AsbCloudInfrastructure/DependencyInjection.cs | 1 + .../Repository/SlipsStatRepository.cs | 173 ++++++++++++++++++ .../Controllers/SlipsStatController.cs | 48 +++++ 6 files changed, 325 insertions(+) create mode 100644 AsbCloudApp/Data/SlipsStatDto.cs create mode 100644 AsbCloudApp/Repositories/ISlipsStatsRepository.cs create mode 100644 AsbCloudApp/Requests/OperationStatRequest.cs create mode 100644 AsbCloudInfrastructure/Repository/SlipsStatRepository.cs create mode 100644 AsbCloudWebApi/Controllers/SlipsStatController.cs diff --git a/AsbCloudApp/Data/SlipsStatDto.cs b/AsbCloudApp/Data/SlipsStatDto.cs new file mode 100644 index 00000000..05843a92 --- /dev/null +++ b/AsbCloudApp/Data/SlipsStatDto.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AsbCloudApp.Data +{ + /// + /// DTO, описывающая аналитику удержания в клиньях + /// + public class SlipsStatDto + { + /// + /// ФИО бурильщика + /// + public string DrillerName { get; set; } = null!; + + /// + /// Количество скважин + /// + public int WellCount { get; set; } + + /// + /// Название секции + /// + public string SectionCaption { get; set; } = null!; + + /// + /// Количество удержаний в клиньях, шт. + /// + public int SlipsCount { get; set; } + + /// + /// Время удержания в клиньях, мин. + /// + public double SlipsTimeInMinutes { get; set; } + + /// + /// Проходка, м. + /// + public double SlipsDepth { get; set; } + } +} diff --git a/AsbCloudApp/Repositories/ISlipsStatsRepository.cs b/AsbCloudApp/Repositories/ISlipsStatsRepository.cs new file mode 100644 index 00000000..644a8a79 --- /dev/null +++ b/AsbCloudApp/Repositories/ISlipsStatsRepository.cs @@ -0,0 +1,23 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Requests; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudApp.Repositories +{ + /// + /// Сервис для получения аналитики удержания в клиньях + /// + public interface ISlipsStatsRepository + { + /// + /// Получение записей для построения аналитики удержания в клиньях + /// + /// параметры запроса + /// + /// + Task> GetAllAsync(OperationStatRequest request, CancellationToken token); + } +} diff --git a/AsbCloudApp/Requests/OperationStatRequest.cs b/AsbCloudApp/Requests/OperationStatRequest.cs new file mode 100644 index 00000000..c249b86b --- /dev/null +++ b/AsbCloudApp/Requests/OperationStatRequest.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace AsbCloudApp.Requests +{ + /// + /// Параметры фильтра операции + /// + public class OperationStatRequest : RequestBase + { + + /// + /// Дата начала периода, за который строится отчет + /// + public DateTime? DateStart { get; set; } + + /// + /// Дата окончания периода, за который строится отчет + /// + public DateTime? DateEnd { get; set; } + + + /// + /// Минимальная продолжительность операции, мину + /// + public int? DurationMinutesMin { get; set; } + + /// + /// Максимальная продолжительность операции, мин + /// + public int? DurationMinutesMax { get; set; } + + + } +} diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 3b480e2d..a0268bd9 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -199,6 +199,7 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient, CrudCacheRepositoryBase>(); diff --git a/AsbCloudInfrastructure/Repository/SlipsStatRepository.cs b/AsbCloudInfrastructure/Repository/SlipsStatRepository.cs new file mode 100644 index 00000000..7e43f000 --- /dev/null +++ b/AsbCloudInfrastructure/Repository/SlipsStatRepository.cs @@ -0,0 +1,173 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; +using AsbCloudDb.Model; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Repository +{ + public class SlipsStatRepository : ISlipsStatsRepository + { + private readonly IAsbCloudDbContext db; + public SlipsStatRepository(IAsbCloudDbContext db) + { + this.db = db; + } + + public async Task> GetAllAsync(OperationStatRequest request, CancellationToken token) + { + if (request.DateStart.HasValue) + request.DateStart = DateTime.SpecifyKind(request.DateStart.Value, DateTimeKind.Utc); + + if (request.DateEnd.HasValue) + request.DateEnd = DateTime.SpecifyKind(request.DateEnd.Value, DateTimeKind.Utc); + + var schedulesQuery = db.Schedule + .Include(s => s.Well) + .Include(s => s.Driller) + .AsNoTracking(); + + if (request.DateStart.HasValue && request.DateEnd.HasValue) + schedulesQuery = schedulesQuery. + Where(s => s.DrillStart >= request.DateStart && s.DrillEnd <= request.DateEnd); + + var schedules = await schedulesQuery.ToArrayAsync(token); + + var wells = schedules + .Select(d => d.Well) + .Where(well => well.IdTelemetry != null) + .GroupBy(w => w.Id) + .ToDictionary(g => g.Key, g => g.First().IdTelemetry!.Value); + + var idsWells = wells.Keys; + var idsTelemetries = wells.Values; + var telemetries = wells.ToDictionary(wt => wt.Value, wt => wt.Key); + + var factWellOperationsQuery = db.WellOperations + .Where(o => idsWells.Contains(o.IdWell)) + .Where(o => o.IdType == 1) + .Where(o => WellOperationCategory.MechanicalDrillingSubIds.Contains(o.IdCategory)) + .Include(o => o.WellSectionType) + .AsNoTracking(); + + if (request.DateStart.HasValue && request.DateEnd.HasValue) + factWellOperationsQuery = factWellOperationsQuery + .Where(o => o.DateStart.AddHours(o.DurationHours) > request.DateStart && o.DateStart < request.DateEnd); + + var factWellOperations = await factWellOperationsQuery.ToArrayAsync(token); + + var sections = factWellOperations + .GroupBy(o => new { o.IdWell, o.IdWellSectionType }) + .Select(g => new + { + g.Key.IdWell, + g.Key.IdWellSectionType, + DepthStart = g.Min(o => o.DepthStart), + DepthEnd = g.Max(o => o.DepthEnd), + g.FirstOrDefault()!.WellSectionType.Caption + }); + + var detectedOperationsQuery = db.DetectedOperations + .Where(o => idsTelemetries.Contains(o.IdTelemetry)) + .Where(o => o.IdCategory == WellOperationCategory.IdSlipsTime) + .AsNoTracking(); + + if (request.DateStart.HasValue && request.DateEnd.HasValue) + detectedOperationsQuery = detectedOperationsQuery + .Where(o => o.DateStart < request.DateEnd) + .Where(o => o.DateEnd > request.DateStart); + + TimeSpan? durationMinutesMin = request.DurationMinutesMin.HasValue + ? new TimeSpan(0, request.DurationMinutesMin.Value, 0) + : null; + TimeSpan? durationMinutesMax = request.DurationMinutesMax.HasValue + ? new TimeSpan(0, request.DurationMinutesMax.Value, 0) + : null; + + if (durationMinutesMin.HasValue && durationMinutesMax.HasValue) + { + detectedOperationsQuery = detectedOperationsQuery + .Where(o => o.DateEnd - o.DateStart >= durationMinutesMin.Value + && o.DateEnd - o.DateStart <= durationMinutesMax.Value); + } + else if (durationMinutesMin.HasValue && !durationMinutesMax.HasValue) + { + detectedOperationsQuery = detectedOperationsQuery + .Where(o => o.DateEnd - o.DateStart >= durationMinutesMin.Value); + } + else if (!durationMinutesMin.HasValue && durationMinutesMax.HasValue) + { + detectedOperationsQuery = detectedOperationsQuery + .Where(o => o.DateEnd - o.DateStart <= durationMinutesMax.Value); + } + + var detectedOperations = await detectedOperationsQuery + .ToArrayAsync(token); + + var detectedOperationsGroupedByDrillerAndSection = detectedOperations.Select(o => new + { + Operation = o, + IdWell = telemetries[o.IdTelemetry], + schedules.FirstOrDefault(s => + s.IdWell == telemetries[o.IdTelemetry] + && s.DrillStart <= o.DateStart + && s.DrillEnd >= o.DateStart + && new TimeDto(s.ShiftStart) <= new TimeDto(o.DateStart.DateTime) + && new TimeDto(s.ShiftEnd) >= new TimeDto(o.DateStart.DateTime)) + ?.Driller, + Section = sections.FirstOrDefault(s => + s.IdWell == telemetries[o.IdTelemetry] + && s.DepthStart <= o.DepthStart + && s.DepthEnd >= o.DepthStart) + }) + .Where(o => o.Driller != null) + .Where(o => o.Section != null) + .Select(o => new + { + o.Operation, + o.IdWell, + Driller = o.Driller!, + Section = o.Section! + }) + .GroupBy(o => new { o.Driller.Id, o.Section.IdWellSectionType }); + + + var factWellOperationsGroupedByDrillerAndSection = factWellOperations + .Select(o => new + { + Operation = o, + schedules.FirstOrDefault(s => + s.IdWell == o.IdWell + && s.DrillStart <= o.DateStart + && s.DrillEnd >= o.DateStart + && new TimeDto(s.ShiftStart) <= new TimeDto(o.DateStart.DateTime) + && new TimeDto(s.ShiftEnd) >= new TimeDto(o.DateStart.DateTime)) + ?.Driller, + }) + .Where(o => o.Driller != null) + .GroupBy(o => new { o.Driller!.Id, o.Operation.IdWellSectionType }); + + + var stats = detectedOperationsGroupedByDrillerAndSection.Select(group => new SlipsStatDto + { + DrillerName = $"{group.First().Driller!.Name} {group.First().Driller!.Patronymic} {group.First().Driller!.Surname}", + SlipsCount = group.Count(), + SlipsTimeInMinutes = group + .Sum(y => (y.Operation.DateEnd - y.Operation.DateStart).TotalMinutes), + SlipsDepth = factWellOperationsGroupedByDrillerAndSection + .Where(o => o.Key.Id == group.Key.Id) + .Where(o => o.Key.IdWellSectionType == group.Key.IdWellSectionType) + .Sum(o => o.Max(op => op.Operation.DepthEnd) - o.Min(op => op.Operation.DepthStart)), + SectionCaption = group.First().Section!.Caption, + WellCount = group.GroupBy(g => g.IdWell).Count(), + }); + + return stats; + } + } +} \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/SlipsStatController.cs b/AsbCloudWebApi/Controllers/SlipsStatController.cs new file mode 100644 index 00000000..f5e1dec8 --- /dev/null +++ b/AsbCloudWebApi/Controllers/SlipsStatController.cs @@ -0,0 +1,48 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; +using AsbCloudDb.Model; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudWebApi.Controllers +{ + /// + /// Аналитика по удержанию в клиньях + /// + [Route("api/slipsStat")] + [ApiController] + [Authorize] + public class SlipsStatController : ControllerBase + { + private readonly ISlipsStatsRepository slipsAnalyticsService; + + public SlipsStatController(ISlipsStatsRepository slipsAnalyticsService) + { + this.slipsAnalyticsService = slipsAnalyticsService; + } + + /// + /// Получить аналитику по удержанию в клиньях (по бурильщикам) + /// + /// Параметры запроса + /// Токен отмены задачи + /// Список бурильщиков + [HttpGet] + [Permission] + [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] + public async Task GetAllAsync( + [FromQuery] OperationStatRequest request, + CancellationToken token) + { + var modal = await slipsAnalyticsService.GetAllAsync(request, token).ConfigureAwait(false); + + return Ok(modal); + } + } +} + From d0846beb68a0e8f344401e393ca99cb83a1ee547 Mon Sep 17 00:00:00 2001 From: Olga Nemt Date: Tue, 3 Oct 2023 09:09:22 +0500 Subject: [PATCH 02/11] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D0=B5=D0=B2=20=D0=B2=20OperationStatRequest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Requests/OperationStatRequest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AsbCloudApp/Requests/OperationStatRequest.cs b/AsbCloudApp/Requests/OperationStatRequest.cs index c249b86b..c2a6806c 100644 --- a/AsbCloudApp/Requests/OperationStatRequest.cs +++ b/AsbCloudApp/Requests/OperationStatRequest.cs @@ -11,18 +11,18 @@ namespace AsbCloudApp.Requests { /// - /// Дата начала периода, за который строится отчет + /// Дата начала операции /// public DateTime? DateStart { get; set; } /// - /// Дата окончания периода, за который строится отчет + /// Дата окончания операции /// public DateTime? DateEnd { get; set; } /// - /// Минимальная продолжительность операции, мину + /// Минимальная продолжительность операции, мин /// public int? DurationMinutesMin { get; set; } From a2502a8cf98927ad629068e879eb6ef0151135e4 Mon Sep 17 00:00:00 2001 From: Olga Nemt Date: Tue, 3 Oct 2023 09:10:03 +0500 Subject: [PATCH 03/11] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B5=20=D1=8E=D0=B7=D0=B8=D0=BD?= =?UTF-8?q?=D0=B3=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Requests/OperationStatRequest.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/AsbCloudApp/Requests/OperationStatRequest.cs b/AsbCloudApp/Requests/OperationStatRequest.cs index c2a6806c..8ab9243e 100644 --- a/AsbCloudApp/Requests/OperationStatRequest.cs +++ b/AsbCloudApp/Requests/OperationStatRequest.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; namespace AsbCloudApp.Requests { From 83b1da7c9cc1338245baebe9e2e97d8b68f6524e Mon Sep 17 00:00:00 2001 From: Olga Nemt Date: Tue, 3 Oct 2023 09:12:26 +0500 Subject: [PATCH 04/11] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B2=20SlipsStatController?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudWebApi/Controllers/SlipsStatController.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/AsbCloudWebApi/Controllers/SlipsStatController.cs b/AsbCloudWebApi/Controllers/SlipsStatController.cs index f5e1dec8..2bedb278 100644 --- a/AsbCloudWebApi/Controllers/SlipsStatController.cs +++ b/AsbCloudWebApi/Controllers/SlipsStatController.cs @@ -1,4 +1,5 @@ using AsbCloudApp.Data; +using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudDb.Model; @@ -39,9 +40,13 @@ namespace AsbCloudWebApi.Controllers [FromQuery] OperationStatRequest request, CancellationToken token) { - var modal = await slipsAnalyticsService.GetAllAsync(request, token).ConfigureAwait(false); + var idUser = User.GetUserId(); - return Ok(modal); + if (!idUser.HasValue) + throw new ForbidException("Не удается вас опознать"); + + var data = await slipsAnalyticsService.GetAllAsync(request, token).ConfigureAwait(false); + return Ok(data); } } } From 3b8b3e38f044bed115a83f65853bf291c1ac9ae1 Mon Sep 17 00:00:00 2001 From: Olga Nemt Date: Tue, 3 Oct 2023 09:15:19 +0500 Subject: [PATCH 05/11] =?UTF-8?q?ISlipsStatsRepository=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B8=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=20?= =?UTF-8?q?=D0=B2=20ISlipsStatRepository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...tatsRepository.cs => ISlipsStatRepository.cs} | 2 +- AsbCloudInfrastructure/DependencyInjection.cs | 16 ++-------------- .../Repository/SlipsStatRepository.cs | 2 +- .../Controllers/SlipsStatController.cs | 4 ++-- 4 files changed, 6 insertions(+), 18 deletions(-) rename AsbCloudApp/Repositories/{ISlipsStatsRepository.cs => ISlipsStatRepository.cs} (94%) diff --git a/AsbCloudApp/Repositories/ISlipsStatsRepository.cs b/AsbCloudApp/Repositories/ISlipsStatRepository.cs similarity index 94% rename from AsbCloudApp/Repositories/ISlipsStatsRepository.cs rename to AsbCloudApp/Repositories/ISlipsStatRepository.cs index 644a8a79..6561b0b6 100644 --- a/AsbCloudApp/Repositories/ISlipsStatsRepository.cs +++ b/AsbCloudApp/Repositories/ISlipsStatRepository.cs @@ -10,7 +10,7 @@ namespace AsbCloudApp.Repositories /// /// Сервис для получения аналитики удержания в клиньях /// - public interface ISlipsStatsRepository + public interface ISlipsStatRepository { /// /// Получение записей для построения аналитики удержания в клиньях diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index fff81d0e..d499ef02 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -26,12 +26,8 @@ using System; using AsbCloudApp.Data.Manuals; using AsbCloudApp.Services.AutoGeneratedDailyReports; using AsbCloudApp.Services.Notifications; -using AsbCloudApp.Services.WellOperationImport; using AsbCloudDb.Model.Manuals; using AsbCloudInfrastructure.Services.AutoGeneratedDailyReports; -using AsbCloudInfrastructure.Services.WellOperationImport; -using AsbCloudInfrastructure.Services.WellOperationImport.FileParser; -using AsbCloudInfrastructure.Services.ProcessMap.ProcessMapWellboreDevelopment; namespace AsbCloudInfrastructure { @@ -123,7 +119,6 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -136,6 +131,7 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -151,7 +147,6 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -204,7 +199,7 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient, CrudCacheRepositoryBase>(); @@ -236,13 +231,6 @@ namespace AsbCloudInfrastructure services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - - services.AddTransient(); - services.AddTransient(); - return services; } diff --git a/AsbCloudInfrastructure/Repository/SlipsStatRepository.cs b/AsbCloudInfrastructure/Repository/SlipsStatRepository.cs index 7e43f000..4ca96b95 100644 --- a/AsbCloudInfrastructure/Repository/SlipsStatRepository.cs +++ b/AsbCloudInfrastructure/Repository/SlipsStatRepository.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Repository { - public class SlipsStatRepository : ISlipsStatsRepository + public class SlipsStatRepository : ISlipsStatRepository { private readonly IAsbCloudDbContext db; public SlipsStatRepository(IAsbCloudDbContext db) diff --git a/AsbCloudWebApi/Controllers/SlipsStatController.cs b/AsbCloudWebApi/Controllers/SlipsStatController.cs index 2bedb278..fe0c7222 100644 --- a/AsbCloudWebApi/Controllers/SlipsStatController.cs +++ b/AsbCloudWebApi/Controllers/SlipsStatController.cs @@ -20,9 +20,9 @@ namespace AsbCloudWebApi.Controllers [Authorize] public class SlipsStatController : ControllerBase { - private readonly ISlipsStatsRepository slipsAnalyticsService; + private readonly ISlipsStatRepository slipsAnalyticsService; - public SlipsStatController(ISlipsStatsRepository slipsAnalyticsService) + public SlipsStatController(ISlipsStatRepository slipsAnalyticsService) { this.slipsAnalyticsService = slipsAnalyticsService; } From 2b400012c688af91be608ef911971be770c02499 Mon Sep 17 00:00:00 2001 From: Olga Nemt Date: Tue, 3 Oct 2023 15:32:58 +0500 Subject: [PATCH 06/11] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=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 --- AsbCloudApp/Data/SlipsStatDto.cs | 2 +- AsbCloudApp/Requests/OperationStatRequest.cs | 8 +-- .../ISlipsStatService.cs} | 4 +- AsbCloudInfrastructure/DependencyInjection.cs | 4 +- .../SlipsStatService.cs} | 59 ++++++++----------- .../Controllers/SlipsStatController.cs | 6 +- 6 files changed, 37 insertions(+), 46 deletions(-) rename AsbCloudApp/{Repositories/ISlipsStatRepository.cs => Services/ISlipsStatService.cs} (90%) rename AsbCloudInfrastructure/{Repository/SlipsStatRepository.cs => Services/SlipsStatService.cs} (73%) diff --git a/AsbCloudApp/Data/SlipsStatDto.cs b/AsbCloudApp/Data/SlipsStatDto.cs index 05843a92..dba3228b 100644 --- a/AsbCloudApp/Data/SlipsStatDto.cs +++ b/AsbCloudApp/Data/SlipsStatDto.cs @@ -39,6 +39,6 @@ namespace AsbCloudApp.Data /// /// Проходка, м. /// - public double SlipsDepth { get; set; } + public double SectionDepth { get; set; } } } diff --git a/AsbCloudApp/Requests/OperationStatRequest.cs b/AsbCloudApp/Requests/OperationStatRequest.cs index 8ab9243e..cda26690 100644 --- a/AsbCloudApp/Requests/OperationStatRequest.cs +++ b/AsbCloudApp/Requests/OperationStatRequest.cs @@ -9,14 +9,14 @@ namespace AsbCloudApp.Requests { /// - /// Дата начала операции + /// Дата начала операции в UTC /// - public DateTime? DateStart { get; set; } + public DateTime? DateStartUTC { get; set; } /// - /// Дата окончания операции + /// Дата окончания операции в UTC /// - public DateTime? DateEnd { get; set; } + public DateTime? DateEndUTC { get; set; } /// diff --git a/AsbCloudApp/Repositories/ISlipsStatRepository.cs b/AsbCloudApp/Services/ISlipsStatService.cs similarity index 90% rename from AsbCloudApp/Repositories/ISlipsStatRepository.cs rename to AsbCloudApp/Services/ISlipsStatService.cs index 6561b0b6..55dd0c0a 100644 --- a/AsbCloudApp/Repositories/ISlipsStatRepository.cs +++ b/AsbCloudApp/Services/ISlipsStatService.cs @@ -5,12 +5,12 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -namespace AsbCloudApp.Repositories +namespace AsbCloudApp.Services { /// /// Сервис для получения аналитики удержания в клиньях /// - public interface ISlipsStatRepository + public interface ISlipsStatService { /// /// Получение записей для построения аналитики удержания в клиньях diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index d499ef02..e2c60158 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -28,6 +28,8 @@ using AsbCloudApp.Services.AutoGeneratedDailyReports; using AsbCloudApp.Services.Notifications; using AsbCloudDb.Model.Manuals; using AsbCloudInfrastructure.Services.AutoGeneratedDailyReports; +using AsbCloudApp.Services.WellOperationImport; +using AsbCloudInfrastructure.Services.WellOperationImport; namespace AsbCloudInfrastructure { @@ -199,7 +201,7 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient, CrudCacheRepositoryBase>(); diff --git a/AsbCloudInfrastructure/Repository/SlipsStatRepository.cs b/AsbCloudInfrastructure/Services/SlipsStatService.cs similarity index 73% rename from AsbCloudInfrastructure/Repository/SlipsStatRepository.cs rename to AsbCloudInfrastructure/Services/SlipsStatService.cs index 4ca96b95..56e422dd 100644 --- a/AsbCloudInfrastructure/Repository/SlipsStatRepository.cs +++ b/AsbCloudInfrastructure/Services/SlipsStatService.cs @@ -1,6 +1,6 @@ using AsbCloudApp.Data; -using AsbCloudApp.Repositories; using AsbCloudApp.Requests; +using AsbCloudApp.Services; using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; using System; @@ -9,32 +9,32 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -namespace AsbCloudInfrastructure.Repository +namespace AsbCloudInfrastructure.Services { - public class SlipsStatRepository : ISlipsStatRepository + public class SlipsStatService : ISlipsStatService { private readonly IAsbCloudDbContext db; - public SlipsStatRepository(IAsbCloudDbContext db) + public SlipsStatService(IAsbCloudDbContext db) { this.db = db; } public async Task> GetAllAsync(OperationStatRequest request, CancellationToken token) { - if (request.DateStart.HasValue) - request.DateStart = DateTime.SpecifyKind(request.DateStart.Value, DateTimeKind.Utc); + if (request.DateStartUTC.HasValue) + request.DateStartUTC = DateTime.SpecifyKind(request.DateStartUTC.Value, DateTimeKind.Utc); - if (request.DateEnd.HasValue) - request.DateEnd = DateTime.SpecifyKind(request.DateEnd.Value, DateTimeKind.Utc); + if (request.DateEndUTC.HasValue) + request.DateEndUTC = DateTime.SpecifyKind(request.DateEndUTC.Value, DateTimeKind.Utc); var schedulesQuery = db.Schedule .Include(s => s.Well) .Include(s => s.Driller) .AsNoTracking(); - if (request.DateStart.HasValue && request.DateEnd.HasValue) + if (request.DateStartUTC.HasValue && request.DateEndUTC.HasValue) schedulesQuery = schedulesQuery. - Where(s => s.DrillStart >= request.DateStart && s.DrillEnd <= request.DateEnd); + Where(s => s.DrillStart >= request.DateStartUTC && s.DrillEnd <= request.DateEndUTC); var schedules = await schedulesQuery.ToArrayAsync(token); @@ -55,9 +55,9 @@ namespace AsbCloudInfrastructure.Repository .Include(o => o.WellSectionType) .AsNoTracking(); - if (request.DateStart.HasValue && request.DateEnd.HasValue) + if (request.DateStartUTC.HasValue && request.DateEndUTC.HasValue) factWellOperationsQuery = factWellOperationsQuery - .Where(o => o.DateStart.AddHours(o.DurationHours) > request.DateStart && o.DateStart < request.DateEnd); + .Where(o => o.DateStart.AddHours(o.DurationHours) > request.DateStartUTC && o.DateStart < request.DateEndUTC); var factWellOperations = await factWellOperationsQuery.ToArrayAsync(token); @@ -69,7 +69,7 @@ namespace AsbCloudInfrastructure.Repository g.Key.IdWellSectionType, DepthStart = g.Min(o => o.DepthStart), DepthEnd = g.Max(o => o.DepthEnd), - g.FirstOrDefault()!.WellSectionType.Caption + g.First().WellSectionType.Caption }); var detectedOperationsQuery = db.DetectedOperations @@ -77,33 +77,22 @@ namespace AsbCloudInfrastructure.Repository .Where(o => o.IdCategory == WellOperationCategory.IdSlipsTime) .AsNoTracking(); - if (request.DateStart.HasValue && request.DateEnd.HasValue) + if (request.DateStartUTC.HasValue && request.DateEndUTC.HasValue) detectedOperationsQuery = detectedOperationsQuery - .Where(o => o.DateStart < request.DateEnd) - .Where(o => o.DateEnd > request.DateStart); + .Where(o => o.DateStart < request.DateEndUTC) + .Where(o => o.DateEnd > request.DateStartUTC); - TimeSpan? durationMinutesMin = request.DurationMinutesMin.HasValue - ? new TimeSpan(0, request.DurationMinutesMin.Value, 0) - : null; - TimeSpan? durationMinutesMax = request.DurationMinutesMax.HasValue - ? new TimeSpan(0, request.DurationMinutesMax.Value, 0) - : null; - - if (durationMinutesMin.HasValue && durationMinutesMax.HasValue) + if (request.DurationMinutesMin.HasValue) { + var durationMinutesMin = new TimeSpan(0, request.DurationMinutesMin.Value, 0); detectedOperationsQuery = detectedOperationsQuery - .Where(o => o.DateEnd - o.DateStart >= durationMinutesMin.Value - && o.DateEnd - o.DateStart <= durationMinutesMax.Value); + .Where(o => o.DateEnd - o.DateStart >= durationMinutesMin); } - else if (durationMinutesMin.HasValue && !durationMinutesMax.HasValue) + if (request.DurationMinutesMax.HasValue) { + var durationMinutesMax = new TimeSpan(0, request.DurationMinutesMax.Value, 0); detectedOperationsQuery = detectedOperationsQuery - .Where(o => o.DateEnd - o.DateStart >= durationMinutesMin.Value); - } - else if (!durationMinutesMin.HasValue && durationMinutesMax.HasValue) - { - detectedOperationsQuery = detectedOperationsQuery - .Where(o => o.DateEnd - o.DateStart <= durationMinutesMax.Value); + .Where(o => o.DateEnd - o.DateStart <= durationMinutesMax); } var detectedOperations = await detectedOperationsQuery @@ -159,11 +148,11 @@ namespace AsbCloudInfrastructure.Repository SlipsCount = group.Count(), SlipsTimeInMinutes = group .Sum(y => (y.Operation.DateEnd - y.Operation.DateStart).TotalMinutes), - SlipsDepth = factWellOperationsGroupedByDrillerAndSection + SectionDepth = factWellOperationsGroupedByDrillerAndSection .Where(o => o.Key.Id == group.Key.Id) .Where(o => o.Key.IdWellSectionType == group.Key.IdWellSectionType) .Sum(o => o.Max(op => op.Operation.DepthEnd) - o.Min(op => op.Operation.DepthStart)), - SectionCaption = group.First().Section!.Caption, + SectionCaption = group.First().Section.Caption, WellCount = group.GroupBy(g => g.IdWell).Count(), }); diff --git a/AsbCloudWebApi/Controllers/SlipsStatController.cs b/AsbCloudWebApi/Controllers/SlipsStatController.cs index fe0c7222..124622a3 100644 --- a/AsbCloudWebApi/Controllers/SlipsStatController.cs +++ b/AsbCloudWebApi/Controllers/SlipsStatController.cs @@ -1,7 +1,7 @@ using AsbCloudApp.Data; using AsbCloudApp.Exceptions; -using AsbCloudApp.Repositories; using AsbCloudApp.Requests; +using AsbCloudApp.Services; using AsbCloudDb.Model; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -20,9 +20,9 @@ namespace AsbCloudWebApi.Controllers [Authorize] public class SlipsStatController : ControllerBase { - private readonly ISlipsStatRepository slipsAnalyticsService; + private readonly ISlipsStatService slipsAnalyticsService; - public SlipsStatController(ISlipsStatRepository slipsAnalyticsService) + public SlipsStatController(ISlipsStatService slipsAnalyticsService) { this.slipsAnalyticsService = slipsAnalyticsService; } From 6eb118e9f62c5cedb8d4b3e501ff559a45a332cf Mon Sep 17 00:00:00 2001 From: Frolov-Nikita Date: Wed, 4 Oct 2023 09:15:28 +0500 Subject: [PATCH 07/11] =?UTF-8?q?#15287262=20=D0=A1=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B0=D0=BB=20OperationStatRequest.DurationMinutes*=20double?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Requests/OperationStatRequest.cs | 10 +- .../Services/SlipsStatService.cs | 253 +++++++++--------- 2 files changed, 130 insertions(+), 133 deletions(-) diff --git a/AsbCloudApp/Requests/OperationStatRequest.cs b/AsbCloudApp/Requests/OperationStatRequest.cs index cda26690..d92d803d 100644 --- a/AsbCloudApp/Requests/OperationStatRequest.cs +++ b/AsbCloudApp/Requests/OperationStatRequest.cs @@ -5,9 +5,8 @@ namespace AsbCloudApp.Requests /// /// Параметры фильтра операции /// - public class OperationStatRequest : RequestBase + public class OperationStatRequest { - /// /// Дата начала операции в UTC /// @@ -18,17 +17,14 @@ namespace AsbCloudApp.Requests /// public DateTime? DateEndUTC { get; set; } - /// /// Минимальная продолжительность операции, мин /// - public int? DurationMinutesMin { get; set; } + public double? DurationMinutesMin { get; set; } /// /// Максимальная продолжительность операции, мин /// - public int? DurationMinutesMax { get; set; } - - + public double? DurationMinutesMax { get; set; } } } diff --git a/AsbCloudInfrastructure/Services/SlipsStatService.cs b/AsbCloudInfrastructure/Services/SlipsStatService.cs index 56e422dd..2458da93 100644 --- a/AsbCloudInfrastructure/Services/SlipsStatService.cs +++ b/AsbCloudInfrastructure/Services/SlipsStatService.cs @@ -9,154 +9,155 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -namespace AsbCloudInfrastructure.Services +namespace AsbCloudInfrastructure.Services; + +public class SlipsStatService : ISlipsStatService { - public class SlipsStatService : ISlipsStatService + private readonly IAsbCloudDbContext db; + + public SlipsStatService(IAsbCloudDbContext db) { - private readonly IAsbCloudDbContext db; - public SlipsStatService(IAsbCloudDbContext db) + this.db = db; + } + + public async Task> GetAllAsync(OperationStatRequest request, CancellationToken token) + { + if (request.DateStartUTC.HasValue) + request.DateStartUTC = DateTime.SpecifyKind(request.DateStartUTC.Value, DateTimeKind.Utc); + + if (request.DateEndUTC.HasValue) + request.DateEndUTC = DateTime.SpecifyKind(request.DateEndUTC.Value, DateTimeKind.Utc); + + var schedulesQuery = db.Schedule + .Include(s => s.Well) + .Include(s => s.Driller) + .AsNoTracking(); + + if (request.DateStartUTC.HasValue && request.DateEndUTC.HasValue) + schedulesQuery = schedulesQuery. + Where(s => s.DrillStart >= request.DateStartUTC && s.DrillEnd <= request.DateEndUTC); + + var schedules = await schedulesQuery.ToArrayAsync(token); + + var wells = schedules + .Select(d => d.Well) + .Where(well => well.IdTelemetry != null) + .GroupBy(w => w.Id) + .ToDictionary(g => g.Key, g => g.First().IdTelemetry!.Value); + + var idsWells = wells.Keys; + var idsTelemetries = wells.Values; + var telemetries = wells.ToDictionary(wt => wt.Value, wt => wt.Key); + + var factWellOperationsQuery = db.WellOperations + .Where(o => idsWells.Contains(o.IdWell)) + .Where(o => o.IdType == 1) + .Where(o => WellOperationCategory.MechanicalDrillingSubIds.Contains(o.IdCategory)) + .Include(o => o.WellSectionType) + .AsNoTracking(); + + if (request.DateStartUTC.HasValue && request.DateEndUTC.HasValue) + factWellOperationsQuery = factWellOperationsQuery + .Where(o => o.DateStart.AddHours(o.DurationHours) > request.DateStartUTC && o.DateStart < request.DateEndUTC); + + var factWellOperations = await factWellOperationsQuery.ToArrayAsync(token); + + var sections = factWellOperations + .GroupBy(o => new { o.IdWell, o.IdWellSectionType }) + .Select(g => new + { + g.Key.IdWell, + g.Key.IdWellSectionType, + DepthStart = g.Min(o => o.DepthStart), + DepthEnd = g.Max(o => o.DepthEnd), + g.First().WellSectionType.Caption + }); + + var detectedOperationsQuery = db.DetectedOperations + .Where(o => idsTelemetries.Contains(o.IdTelemetry)) + .Where(o => o.IdCategory == WellOperationCategory.IdSlipsTime) + .AsNoTracking(); + + if (request.DateStartUTC.HasValue && request.DateEndUTC.HasValue) + detectedOperationsQuery = detectedOperationsQuery + .Where(o => o.DateStart < request.DateEndUTC) + .Where(o => o.DateEnd > request.DateStartUTC); + + if (request.DurationMinutesMin.HasValue) { - this.db = db; + var durationMinutesMin = TimeSpan.FromMinutes(request.DurationMinutesMin.Value); + detectedOperationsQuery = detectedOperationsQuery + .Where(o => o.DateEnd - o.DateStart >= durationMinutesMin); } - public async Task> GetAllAsync(OperationStatRequest request, CancellationToken token) + if (request.DurationMinutesMax.HasValue) { - if (request.DateStartUTC.HasValue) - request.DateStartUTC = DateTime.SpecifyKind(request.DateStartUTC.Value, DateTimeKind.Utc); + var durationMinutesMax = TimeSpan.FromMinutes(request.DurationMinutesMax.Value); + detectedOperationsQuery = detectedOperationsQuery + .Where(o => o.DateEnd - o.DateStart <= durationMinutesMax); + } - if (request.DateEndUTC.HasValue) - request.DateEndUTC = DateTime.SpecifyKind(request.DateEndUTC.Value, DateTimeKind.Utc); + var detectedOperations = await detectedOperationsQuery + .ToArrayAsync(token); - var schedulesQuery = db.Schedule - .Include(s => s.Well) - .Include(s => s.Driller) - .AsNoTracking(); - - if (request.DateStartUTC.HasValue && request.DateEndUTC.HasValue) - schedulesQuery = schedulesQuery. - Where(s => s.DrillStart >= request.DateStartUTC && s.DrillEnd <= request.DateEndUTC); - - var schedules = await schedulesQuery.ToArrayAsync(token); - - var wells = schedules - .Select(d => d.Well) - .Where(well => well.IdTelemetry != null) - .GroupBy(w => w.Id) - .ToDictionary(g => g.Key, g => g.First().IdTelemetry!.Value); - - var idsWells = wells.Keys; - var idsTelemetries = wells.Values; - var telemetries = wells.ToDictionary(wt => wt.Value, wt => wt.Key); - - var factWellOperationsQuery = db.WellOperations - .Where(o => idsWells.Contains(o.IdWell)) - .Where(o => o.IdType == 1) - .Where(o => WellOperationCategory.MechanicalDrillingSubIds.Contains(o.IdCategory)) - .Include(o => o.WellSectionType) - .AsNoTracking(); - - if (request.DateStartUTC.HasValue && request.DateEndUTC.HasValue) - factWellOperationsQuery = factWellOperationsQuery - .Where(o => o.DateStart.AddHours(o.DurationHours) > request.DateStartUTC && o.DateStart < request.DateEndUTC); - - var factWellOperations = await factWellOperationsQuery.ToArrayAsync(token); - - var sections = factWellOperations - .GroupBy(o => new { o.IdWell, o.IdWellSectionType }) - .Select(g => new - { - g.Key.IdWell, - g.Key.IdWellSectionType, - DepthStart = g.Min(o => o.DepthStart), - DepthEnd = g.Max(o => o.DepthEnd), - g.First().WellSectionType.Caption - }); - - var detectedOperationsQuery = db.DetectedOperations - .Where(o => idsTelemetries.Contains(o.IdTelemetry)) - .Where(o => o.IdCategory == WellOperationCategory.IdSlipsTime) - .AsNoTracking(); - - if (request.DateStartUTC.HasValue && request.DateEndUTC.HasValue) - detectedOperationsQuery = detectedOperationsQuery - .Where(o => o.DateStart < request.DateEndUTC) - .Where(o => o.DateEnd > request.DateStartUTC); - - if (request.DurationMinutesMin.HasValue) + var detectedOperationsGroupedByDrillerAndSection = detectedOperations.Select(o => new + { + Operation = o, + IdWell = telemetries[o.IdTelemetry], + schedules.FirstOrDefault(s => + s.IdWell == telemetries[o.IdTelemetry] + && s.DrillStart <= o.DateStart + && s.DrillEnd >= o.DateStart + && new TimeDto(s.ShiftStart) <= new TimeDto(o.DateStart.DateTime) + && new TimeDto(s.ShiftEnd) >= new TimeDto(o.DateStart.DateTime)) + ?.Driller, + Section = sections.FirstOrDefault(s => + s.IdWell == telemetries[o.IdTelemetry] + && s.DepthStart <= o.DepthStart + && s.DepthEnd >= o.DepthStart) + }) + .Where(o => o.Driller != null) + .Where(o => o.Section != null) + .Select(o => new { - var durationMinutesMin = new TimeSpan(0, request.DurationMinutesMin.Value, 0); - detectedOperationsQuery = detectedOperationsQuery - .Where(o => o.DateEnd - o.DateStart >= durationMinutesMin); - } - if (request.DurationMinutesMax.HasValue) - { - var durationMinutesMax = new TimeSpan(0, request.DurationMinutesMax.Value, 0); - detectedOperationsQuery = detectedOperationsQuery - .Where(o => o.DateEnd - o.DateStart <= durationMinutesMax); - } + o.Operation, + o.IdWell, + Driller = o.Driller!, + Section = o.Section! + }) + .GroupBy(o => new { o.Driller.Id, o.Section.IdWellSectionType }); - var detectedOperations = await detectedOperationsQuery - .ToArrayAsync(token); - var detectedOperationsGroupedByDrillerAndSection = detectedOperations.Select(o => new + var factWellOperationsGroupedByDrillerAndSection = factWellOperations + .Select(o => new { Operation = o, - IdWell = telemetries[o.IdTelemetry], schedules.FirstOrDefault(s => - s.IdWell == telemetries[o.IdTelemetry] + s.IdWell == o.IdWell && s.DrillStart <= o.DateStart && s.DrillEnd >= o.DateStart && new TimeDto(s.ShiftStart) <= new TimeDto(o.DateStart.DateTime) && new TimeDto(s.ShiftEnd) >= new TimeDto(o.DateStart.DateTime)) ?.Driller, - Section = sections.FirstOrDefault(s => - s.IdWell == telemetries[o.IdTelemetry] - && s.DepthStart <= o.DepthStart - && s.DepthEnd >= o.DepthStart) }) - .Where(o => o.Driller != null) - .Where(o => o.Section != null) - .Select(o => new - { - o.Operation, - o.IdWell, - Driller = o.Driller!, - Section = o.Section! - }) - .GroupBy(o => new { o.Driller.Id, o.Section.IdWellSectionType }); + .Where(o => o.Driller != null) + .GroupBy(o => new { o.Driller!.Id, o.Operation.IdWellSectionType }); - var factWellOperationsGroupedByDrillerAndSection = factWellOperations - .Select(o => new - { - Operation = o, - schedules.FirstOrDefault(s => - s.IdWell == o.IdWell - && s.DrillStart <= o.DateStart - && s.DrillEnd >= o.DateStart - && new TimeDto(s.ShiftStart) <= new TimeDto(o.DateStart.DateTime) - && new TimeDto(s.ShiftEnd) >= new TimeDto(o.DateStart.DateTime)) - ?.Driller, - }) - .Where(o => o.Driller != null) - .GroupBy(o => new { o.Driller!.Id, o.Operation.IdWellSectionType }); + var stats = detectedOperationsGroupedByDrillerAndSection.Select(group => new SlipsStatDto + { + DrillerName = $"{group.First().Driller!.Name} {group.First().Driller!.Patronymic} {group.First().Driller!.Surname}", + SlipsCount = group.Count(), + SlipsTimeInMinutes = group + .Sum(y => (y.Operation.DateEnd - y.Operation.DateStart).TotalMinutes), + SectionDepth = factWellOperationsGroupedByDrillerAndSection + .Where(o => o.Key.Id == group.Key.Id) + .Where(o => o.Key.IdWellSectionType == group.Key.IdWellSectionType) + .Sum(o => o.Max(op => op.Operation.DepthEnd) - o.Min(op => op.Operation.DepthStart)), + SectionCaption = group.First().Section.Caption, + WellCount = group.GroupBy(g => g.IdWell).Count(), + }); - - var stats = detectedOperationsGroupedByDrillerAndSection.Select(group => new SlipsStatDto - { - DrillerName = $"{group.First().Driller!.Name} {group.First().Driller!.Patronymic} {group.First().Driller!.Surname}", - SlipsCount = group.Count(), - SlipsTimeInMinutes = group - .Sum(y => (y.Operation.DateEnd - y.Operation.DateStart).TotalMinutes), - SectionDepth = factWellOperationsGroupedByDrillerAndSection - .Where(o => o.Key.Id == group.Key.Id) - .Where(o => o.Key.IdWellSectionType == group.Key.IdWellSectionType) - .Sum(o => o.Max(op => op.Operation.DepthEnd) - o.Min(op => op.Operation.DepthStart)), - SectionCaption = group.First().Section.Caption, - WellCount = group.GroupBy(g => g.IdWell).Count(), - }); - - return stats; - } + return stats; } } \ No newline at end of file From c4b20bbf38787a9623dc9fc50ba63628668796a8 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?= Date: Wed, 4 Oct 2023 09:57:23 +0500 Subject: [PATCH 08/11] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B3=D0=B8=D1=81=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B5?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudInfrastructure/DependencyInjection.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index e2c60158..36a91cf3 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -30,6 +30,8 @@ using AsbCloudDb.Model.Manuals; using AsbCloudInfrastructure.Services.AutoGeneratedDailyReports; using AsbCloudApp.Services.WellOperationImport; using AsbCloudInfrastructure.Services.WellOperationImport; +using AsbCloudInfrastructure.Services.ProcessMap.ProcessMapWellboreDevelopment; +using AsbCloudInfrastructure.Services.WellOperationImport.FileParser; namespace AsbCloudInfrastructure { @@ -119,6 +121,8 @@ namespace AsbCloudInfrastructure services.AddSingleton(provider => ReduceSamplingService.GetInstance(configuration)); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -233,6 +237,13 @@ namespace AsbCloudInfrastructure services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + services.AddTransient(); + services.AddTransient(); + return services; } From 2d375436afb11ad1a02c30a053b69cbb9175bcea Mon Sep 17 00:00:00 2001 From: Olga Nemt Date: Wed, 4 Oct 2023 10:25:05 +0500 Subject: [PATCH 09/11] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=B1=D0=B0?= =?UTF-8?q?=D0=B3=D0=B0=20=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8=20=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=B0=20=D1=81?= =?UTF-8?q?=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Data/MessageDto.cs | 2 +- AsbCloudInfrastructure/Services/SAUB/MessageService.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AsbCloudApp/Data/MessageDto.cs b/AsbCloudApp/Data/MessageDto.cs index 0cdc70c4..6f2036be 100644 --- a/AsbCloudApp/Data/MessageDto.cs +++ b/AsbCloudApp/Data/MessageDto.cs @@ -14,7 +14,7 @@ namespace AsbCloudApp.Data /// /// дата появления события /// - public DateTime Date { get; set; } + public DateTime DateTime { get; set; } /// /// категория события diff --git a/AsbCloudInfrastructure/Services/SAUB/MessageService.cs b/AsbCloudInfrastructure/Services/SAUB/MessageService.cs index 82f0abfc..350829e1 100644 --- a/AsbCloudInfrastructure/Services/SAUB/MessageService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/MessageService.cs @@ -111,7 +111,7 @@ namespace AsbCloudInfrastructure.Services.SAUB WellDepth = message.WellDepth }; - messageDto.Date = message.DateTime.ToRemoteDateTime(timezone.Hours); + messageDto.DateTime = message.DateTime.ToRemoteDateTime(timezone.Hours); if (message.IdTelemetryUser is not null) { From d2f6d468ab1ad62cd40662c96f783daccebbb0e0 Mon Sep 17 00:00:00 2001 From: Frolov-Nikita Date: Wed, 4 Oct 2023 11:55:11 +0500 Subject: [PATCH 10/11] fix SubsystemOperationTimeService.GetDepthIntervalSubsystem() --- .../Services/Subsystems/SubsystemOperationTimeService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs index a1abde3a..f3b079eb 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs @@ -207,7 +207,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems { var depthIntervalSubsystem = 0d; //AKB - MSE - if (idSubsystem == IdSubsystemAKB | idSubsystem == IdSubsystemMSE) + if (idSubsystem == IdSubsystemAKB || idSubsystem == IdSubsystemMSE) { depthIntervalSubsystem = depthInterval.depthIntervalRotor + depthInterval.depthIntervalSlide; } From ee22408225ae6408d970348d3a4c282e3a4e522f Mon Sep 17 00:00:00 2001 From: Frolov-Nikita Date: Wed, 4 Oct 2023 16:41:19 +0500 Subject: [PATCH 11/11] =?UTF-8?q?#20644813=20=D1=80=D0=BA=D1=84=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20SubsystemOperation?= =?UTF-8?q?TimeService=20#20369896=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=81?= =?UTF-8?q?=D0=B2=D0=BE=D0=B9=D1=81=D1=82=D0=B2=D0=B0=20=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D1=82=D0=B8=D1=81=D1=82=D0=B8=D0=BA=D0=B8=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=BD=D0=B0=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B5=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DetectedOperation/OperationsSummaryDto.cs | 32 + .../Data/Subsystems/SubsystemStatDto.cs | 15 +- .../DetectedOperationSummaryRequest.cs | 53 ++ .../Requests/SubsystemOperationTimeRequest.cs | 3 +- .../Services/IDetectedOperationService.cs | 6 +- .../ISubsystemOperationTimeService.cs | 4 +- .../DetectedOperationService.cs | 69 +- .../SubsystemOperationTimeService.cs | 750 +++++++++--------- 8 files changed, 524 insertions(+), 408 deletions(-) create mode 100644 AsbCloudApp/Data/DetectedOperation/OperationsSummaryDto.cs create mode 100644 AsbCloudApp/Requests/DetectedOperationSummaryRequest.cs diff --git a/AsbCloudApp/Data/DetectedOperation/OperationsSummaryDto.cs b/AsbCloudApp/Data/DetectedOperation/OperationsSummaryDto.cs new file mode 100644 index 00000000..74f755cc --- /dev/null +++ b/AsbCloudApp/Data/DetectedOperation/OperationsSummaryDto.cs @@ -0,0 +1,32 @@ +namespace AsbCloudApp.Data.DetectedOperation; + +/// +/// Статистика по операциям +/// +public class OperationsSummaryDto +{ + /// + /// Id телеметрии + /// + public int IdTelemetry { get; set; } + + /// + /// Id названия/описания операции + /// + public int IdCategory { get; set; } + + /// + /// Количество операций + /// + public int Count { get; set; } + + /// + /// Cумма проходок операций + /// + public double SumDepthIntervals { get; set; } + + /// + /// Cумма продолжительностей операций + /// + public double SumDurationHours { get; set; } +} diff --git a/AsbCloudApp/Data/Subsystems/SubsystemStatDto.cs b/AsbCloudApp/Data/Subsystems/SubsystemStatDto.cs index aec6c6b8..da9c98cb 100644 --- a/AsbCloudApp/Data/Subsystems/SubsystemStatDto.cs +++ b/AsbCloudApp/Data/Subsystems/SubsystemStatDto.cs @@ -23,13 +23,20 @@ namespace AsbCloudApp.Data.Subsystems /// public double KUsage { get; set; } /// - /// сумма изменения глубин + /// сумма изменения глубин при включеной подсистеме /// public double SumDepthInterval { get; set; } /// - /// количество операций + /// сумма проходок автоопределенных операций выполняемых подсистемой /// - public int OperationCount { get; set; } - + public double SumOperationDepthInterval { get; set; } + /// + /// сумма продолжительности автоопределенных операций выполняемых подсистемой + /// + public double SumOperationDurationHours { get; set; } + /// + /// количество включений подсистемы + /// + public int OperationCount { get; set; } } } diff --git a/AsbCloudApp/Requests/DetectedOperationSummaryRequest.cs b/AsbCloudApp/Requests/DetectedOperationSummaryRequest.cs new file mode 100644 index 00000000..2b362f28 --- /dev/null +++ b/AsbCloudApp/Requests/DetectedOperationSummaryRequest.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AsbCloudApp.Requests; + +/// +/// Запрос на получение обобщенных данных по операцим +/// +public class DetectedOperationSummaryRequest +{ + /// + /// Список id телеметрий + /// пустой список - нет фильтрации + /// + public IEnumerable IdsTelemetries { get;set;} = Enumerable.Empty(); + + /// + /// Список id категорий операций + /// пустой список - нет фильтрации + /// + public IEnumerable IdsOperationCategories { get; set; } = Enumerable.Empty(); + + /// + /// Больше или равно даты начала операции + /// + public DateTimeOffset? GeDateStart {get;set;} + + /// + /// Меньше или равно даты начала операции + /// + public DateTimeOffset? LeDateStart { get; set; } + + /// + /// Меньше или равно даты окончания операции + /// + public DateTimeOffset? LeDateEnd { get; set; } + + /// + /// Больше или равно глубины начала операции + /// + public double? GeDepthStart { get; set; } + + /// + /// Меньше или равно глубины начала операции + /// + public double? LeDepthStart { get; set; } + + /// + /// Меньше или равно глубины окончания операции + /// + public double? LeDepthEnd { get; set; } +} diff --git a/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs b/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs index 9080b596..02db1042 100644 --- a/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs +++ b/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs @@ -26,7 +26,7 @@ namespace AsbCloudApp.Requests /// /// Больше или равно дате /// - public DateTime? GtDate { get; set; } + public DateTime? GtDate { get; set; }//TODO: its Ge* /// /// Меньше или равно дате @@ -43,6 +43,7 @@ namespace AsbCloudApp.Requests /// public double? LtDepth { get; set; } + //TODO: Replace modes by DateTimeOffset LeDateStart, LeDateEnd /// /// информация попадает в выборку, если интервал выборки частично или полностью пересекается с запрашиваемым интервалом /// diff --git a/AsbCloudApp/Services/IDetectedOperationService.cs b/AsbCloudApp/Services/IDetectedOperationService.cs index 901f7b3f..120cfef8 100644 --- a/AsbCloudApp/Services/IDetectedOperationService.cs +++ b/AsbCloudApp/Services/IDetectedOperationService.cs @@ -42,12 +42,10 @@ namespace AsbCloudApp.Services /// /// Получить интервалы глубин по всем скважинам /// - /// список ИД телеметрий активных скважин - /// - /// + /// /// /// кортеж - ид телеметрии, интервалы глубины забоя (ротор,слайд) - Task> GetDepthIntervalAllOperationsAsync(IEnumerable telemetryIds,DateTimeOffset gtDate, DateTimeOffset ltDate, CancellationToken token); + Task> GetOperationSummaryAsync(DetectedOperationSummaryRequest request, CancellationToken token); /// /// Удалить операции diff --git a/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs b/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs index 774b8754..67b29673 100644 --- a/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs +++ b/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs @@ -19,7 +19,7 @@ namespace AsbCloudApp.Services.Subsystems /// /// /// - Task?> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token); + Task> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token); /// /// Удаление наработки по подсистемам. @@ -37,7 +37,7 @@ namespace AsbCloudApp.Services.Subsystems /// /// /// - Task?> GetOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token); + Task> GetOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token); /// /// Временной диапазон за который есть статистика работы подсистем diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs index 701cfd08..c3246e05 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs @@ -67,27 +67,58 @@ namespace AsbCloudInfrastructure.Services.DetectOperations return dtos; } - public async Task> GetDepthIntervalAllOperationsAsync(IEnumerable telemetryIds, DateTimeOffset gtDate, DateTimeOffset ltDate, CancellationToken token) + public async Task> GetOperationSummaryAsync(DetectedOperationSummaryRequest request, CancellationToken token) { var query = db.Set() - .Include(o => o.OperationCategory) - .Where(o => o.DateStart >= gtDate) - .Where(o => o.DateEnd <= ltDate) - .Where(o => telemetryIds.Contains(o.IdTelemetry)) - .GroupBy(g => g.IdTelemetry) - .Select(g => new - { - IdTelemetry = g.Key, - RotorDepthInterval = g.Where(o => o.IdCategory == WellOperationCategory.IdRotor).Sum(o => o.DepthEnd - o.DepthStart), - SlideDepthInterval = g.Where(o => o.IdCategory == WellOperationCategory.IdSlide).Sum(o => o.DepthEnd - o.DepthStart) - }); - var data = await query.ToArrayAsync(token); - var result = data.Select(g => - ( - g.IdTelemetry, - g.RotorDepthInterval, - g.SlideDepthInterval - )); + .AsNoTracking(); + + if (request.IdsTelemetries.Any()) + query = query.Where(operation => request.IdsTelemetries.Contains(operation.IdTelemetry)); + + if (request.IdsOperationCategories.Any()) + query = query.Where(operation => request.IdsOperationCategories.Contains(operation.IdCategory)); + + if (request.GeDateStart.HasValue) + { + var geDateStart = request.GeDateStart.Value.ToUniversalTime(); + query = query.Where(operation => operation.DateStart >= geDateStart); + } + + if (request.LeDateStart.HasValue) + { + var leDateStart = request.LeDateStart.Value.ToUniversalTime(); + query = query.Where(operation => operation.DateStart <= leDateStart); + } + + if (request.LeDateEnd.HasValue) + { + var leDateEnd = request.LeDateEnd.Value.ToUniversalTime(); + query = query.Where(operation => operation.DateEnd <= leDateEnd); + } + + if (request.GeDepthStart.HasValue) + query = query.Where(operation => operation.DepthStart >= request.GeDepthStart.Value); + + if (request.LeDepthStart.HasValue) + query = query.Where(operation => operation.DepthStart <= request.LeDepthStart.Value); + + if (request.LeDepthEnd.HasValue) + query = query.Where(operation => operation.DepthEnd <= request.LeDepthEnd.Value); + + var queryGroup = query + .GroupBy(operation => new { operation.IdTelemetry, operation.IdCategory }) + .Select(group => new OperationsSummaryDto + { + IdTelemetry = group.Key.IdTelemetry, + IdCategory = group.Key.IdCategory, + Count = group.Count(), + SumDepthIntervals = group.Sum(operation => operation.DepthEnd - operation.DepthStart), + SumDurationHours = group.Sum(operation => (operation.DateEnd - operation.DateStart).TotalHours) + }) + .OrderBy(summ => summ.IdTelemetry) + .ThenBy(summ => summ.IdCategory); + + var result = await queryGroup.ToArrayAsync(token); return result; } diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs index f3b079eb..e40c1a4d 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs @@ -16,385 +16,379 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -namespace AsbCloudInfrastructure.Services.Subsystems +namespace AsbCloudInfrastructure.Services.Subsystems; + +internal class SubsystemOperationTimeService : ISubsystemOperationTimeService { - internal class SubsystemOperationTimeService : ISubsystemOperationTimeService + private readonly IAsbCloudDbContext db; + private readonly IWellService wellService; + private readonly ICrudRepository subsystemService; + private readonly IDetectedOperationService detectedOperationService; + public const int IdSubsystemAKB = 1; + public const int IdSubsystemAKBRotor = 11; + public const int IdSubsystemAKBSlide = 12; + public const int IdSubsystemMSE = 2; + public const int IdSubsystemSpin = 65536; + public const int IdSubsystemTorque = 65537; + + public SubsystemOperationTimeService(IAsbCloudDbContext db, IWellService wellService, ICrudRepository subsystemService, IDetectedOperationService detectedOperationService) { - private readonly IAsbCloudDbContext db; - private readonly IWellService wellService; - private readonly ICrudRepository subsystemService; - private readonly IDetectedOperationService detectedOperationService; - public const int IdSubsystemAKB = 1; - public const int IdSubsystemAKBRotor = 11; - public const int IdSubsystemAKBSlide = 12; - public const int IdSubsystemMSE = 2; - public const int IdSubsystemSpin = 65536; - public const int IdSubsystemTorque = 65537; - public SubsystemOperationTimeService(IAsbCloudDbContext db, IWellService wellService, ICrudRepository subsystemService, IDetectedOperationService detectedOperationService) - { - this.db = db; - this.wellService = wellService; - this.subsystemService = subsystemService; - this.detectedOperationService = detectedOperationService; - } - - /// - public async Task DeleteAsync(SubsystemOperationTimeRequest request, CancellationToken token) - { - var well = await wellService.GetOrDefaultAsync(request.IdWell, token); - if (well?.IdTelemetry is null || well.Timezone is null) - return 0; - var query = BuildQuery(request); - if (query is null) - return 0; - db.SubsystemOperationTimes.RemoveRange(query); - return await db.SaveChangesAsync(token); - } - - /// - public async Task?> GetOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token) - { - var well = await wellService.GetOrDefaultAsync(request.IdWell, token); - if (well?.IdTelemetry is null || well.Timezone is null) - return null; - - var query = BuildQuery(request); - - if (query is null) - return null; - - IEnumerable data = await query.ToListAsync(token); - - if (request.SelectMode == SubsystemOperationTimeRequest.SelectModeInner) - { - if (request.GtDate is not null) - data = data.Where(o => o.DateStart >= request.GtDate.Value); - - if (request.LtDate is not null) - data = data.Where(o => o.DateEnd <= request.LtDate.Value); - } - else if (request.SelectMode == SubsystemOperationTimeRequest.SelectModeTrim) - { - var begin = request.GtDate?.ToUtcDateTimeOffset(well.Timezone.Hours); - var end = request.LtDate?.ToUtcDateTimeOffset(well.Timezone.Hours); - data = TrimOperation(data, begin, end); - } - - var dtos = data.Select(o => Convert(o, well.Timezone.Hours)); - return dtos; - } - - /// - public async Task?> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token) - { - request.SelectMode = SubsystemOperationTimeRequest.SelectModeTrim; - var data = await GetOperationTimeAsync(request, token); - if (data is null) - return null; - - var detectedOperationsRequest = new DetectedOperationRequest() - { - IdWell = request.IdWell, - IdsCategories = new int[] { - WellOperationCategory.IdRotor, WellOperationCategory.IdSlide, - }, - LtDate = request.LtDate, - GtDate = request.GtDate, - }; - var detectedOperations = await detectedOperationService.GetOperationsAsync(detectedOperationsRequest, token); - if(detectedOperations?.Any() != true) - return null; - var depthInterval = GetDepthInterval(detectedOperations); - - var statList = CalcStat(data,depthInterval); - return statList; - } - - private static IEnumerable TrimOperation(IEnumerable data, DateTimeOffset? gtDate, DateTimeOffset? ltDate) - { - if (!ltDate.HasValue && !gtDate.HasValue) - return data.Select(d => d.Adapt()); - - var items = data.Select((item) => - { - var operationTime = item.Adapt(); - if (!(item.DepthStart.HasValue && item.DepthEnd.HasValue)) - return operationTime; - - var dateDiff = (item.DateEnd - item.DateStart).TotalSeconds; - var depthDiff = item.DepthEnd.Value - item.DepthStart.Value; - var a = depthDiff / dateDiff; - var b = item.DepthStart.Value; - - if (gtDate.HasValue && item.DateStart < gtDate.Value) - { - operationTime.DateStart = gtDate.Value; - var x = (gtDate.Value - item.DateStart).TotalSeconds; - operationTime.DepthStart = (float)(a * x + b); - } - if (ltDate.HasValue && item.DateEnd > ltDate.Value) - { - operationTime.DateEnd = ltDate.Value; - var x = (ltDate.Value - item.DateStart).TotalSeconds; - operationTime.DepthEnd = (float)(a * x + b); - } - return operationTime; - }); - - return items; - } - - private IEnumerable CalcStat( - IEnumerable dtos, - (double depthIntervalRotor, double depthIntervalSlide) depthInterval) - { - var groupedDataSubsystems = dtos - .OrderBy(o => o.Id) - .GroupBy(o => o.IdSubsystem); - var periodGroupTotal = dtos.Sum(o => (o.DateEnd - o.DateStart).TotalHours); - - var result = groupedDataSubsystems.Select(g => - { - var depthIntervalSubsystem = GetDepthIntervalSubsystem(g.Key, depthInterval); - var periodGroup = g.Sum(o => (o.DateEnd - o.DateStart).TotalHours); - var periodGroupDepth = g.Sum(o => o.DepthEnd - o.DepthStart); - var subsystemStat = new SubsystemStatDto() - { - IdSubsystem = g.Key, - SubsystemName = subsystemService.GetOrDefault(g.Key)?.Name ?? "unknown", - UsedTimeHours = periodGroup, - //% использования = суммарная проходка АПД в слайде - /// суммарную проходку автоопределенных операций в слайде. - KUsage = periodGroupDepth / depthIntervalSubsystem, - SumDepthInterval = periodGroupDepth, - OperationCount = g.Count(), - }; - if (subsystemStat.KUsage > 1) - subsystemStat.KUsage = 1; - return subsystemStat; - }); - - var apdParts = result.Where(x => x.IdSubsystem == 11 || x.IdSubsystem == 12); - if (apdParts.Any()) - { - var apdSum = new SubsystemStatDto() - { - IdSubsystem = IdSubsystemAKB, - SubsystemName = "АПД", - UsedTimeHours = apdParts.Sum(part => part.UsedTimeHours), - KUsage = apdParts.Sum(part => part.SumDepthInterval) / GetDepthIntervalSubsystem(IdSubsystemAKB, depthInterval), - SumDepthInterval = apdParts.Sum(part => part.SumDepthInterval), - OperationCount = apdParts.Sum(part => part.OperationCount), - }; - result = result.Append(apdSum).OrderBy(m => m.IdSubsystem); - } - - return result; - } - - private static (double depthIntervalRotor, double depthIntervalSlide) GetDepthInterval (IEnumerable detectedOperations) - { - var depthIntervalRotor = detectedOperations.Where(o => o.IdCategory == WellOperationCategory.IdRotor) - .Sum(o => o.DepthEnd - o.DepthStart); - var depthIntervalSlide = detectedOperations.Where(o => o.IdCategory == WellOperationCategory.IdSlide) - .Sum(o => o.DepthEnd - o.DepthStart); - var depthInterval = (depthIntervalRotor, depthIntervalSlide); - - return depthInterval; - } - - private static double GetDepthIntervalSubsystem(int idSubsystem, (double depthIntervalRotor, double depthIntervalSlide) depthInterval) - { - var depthIntervalSubsystem = 0d; - //AKB - MSE - if (idSubsystem == IdSubsystemAKB || idSubsystem == IdSubsystemMSE) - { - depthIntervalSubsystem = depthInterval.depthIntervalRotor + depthInterval.depthIntervalSlide; - } - //AKB - Rotor - if (idSubsystem == IdSubsystemAKBRotor) - { - depthIntervalSubsystem = depthInterval.depthIntervalRotor; - } - //AKB - Slide - if (idSubsystem == IdSubsystemAKBSlide) - { - depthIntervalSubsystem = depthInterval.depthIntervalSlide; - } - //Spin - if (idSubsystem == IdSubsystemSpin) - { - depthIntervalSubsystem = depthInterval.depthIntervalSlide; - } - //Torque - if (idSubsystem == IdSubsystemTorque) - { - depthIntervalSubsystem = depthInterval.depthIntervalRotor; - } - return depthIntervalSubsystem; - - } - - /// - public async Task> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token) - { - var activeWells = await wellService.GetAsync(new() { IdCompany = idCompany, IdState = 1 }, token); - var result = await GetStatAsync(activeWells, gtDate, ltDate, token); - return result; - } - - /// - public async Task> GetStatByActiveWells(IEnumerable wellIds, CancellationToken token) - { - var activeWells = await wellService.GetAsync(new() { Ids = wellIds, IdState = 1 }, token); - var result = await GetStatAsync(activeWells, null, null, token); - return result; - } - - private async Task> GetStatAsync(IEnumerable wells, DateTime? gtDate, DateTime? ltDate, CancellationToken token) - { - if (!wells.Any()) - return Enumerable.Empty(); - - var hoursOffset = wells - .FirstOrDefault(well => well.Timezone is not null) - ?.Timezone.Hours - ?? 5d; - - var beginUTC = gtDate.HasValue - ? gtDate.Value.ToUtcDateTimeOffset(hoursOffset) - : db.SubsystemOperationTimes.Min(s => s.DateStart) - .DateTime - .ToUtcDateTimeOffset(hoursOffset); - - var endUTC = ltDate.HasValue - ? ltDate.Value.ToUtcDateTimeOffset(hoursOffset) - : db.SubsystemOperationTimes.Max(s => s.DateEnd) - .DateTime - .ToUtcDateTimeOffset(hoursOffset); - - var telemetryIds = wells - .Where(w => w.IdTelemetry is not null) - .Select(w => w.IdTelemetry) - .Distinct(); - - var query = db.SubsystemOperationTimes - .Where(o => telemetryIds.Contains(o.IdTelemetry) && - o.DateStart >= beginUTC && - o.DateEnd <= endUTC) - .AsNoTracking(); - - var subsystemsOperationTime = await query.ToListAsync(token); - - var depthIntervals = await detectedOperationService - .GetDepthIntervalAllOperationsAsync(telemetryIds, beginUTC, endUTC, token); - - var result = wells - .Select(well => { - var dtos = subsystemsOperationTime - .Where(s => s.IdTelemetry == well.IdTelemetry) - .Select(s => Convert(s, well.Timezone.Hours)); - - var (idTelemetry, depthIntervalRotor, depthIntervalSlide) = depthIntervals - .FirstOrDefault(i => i.idTelemetry == well.IdTelemetry); - - var subsystemStat = idTelemetry > 0 && dtos.Any() - ? CalcStat(dtos, (depthIntervalRotor, depthIntervalSlide)) - : Enumerable.Empty(); - - return new SubsystemActiveWellStatDto - { - Well = well, - SubsystemAKB = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAKB), - SubsystemMSE = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemMSE), - SubsystemSpinMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemSpin), - SubsystemTorqueMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemTorque), - }; - }); - - return result; - } - - /// - public async Task GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token) - { - var query = BuildQuery(request); - if (query is null) - { - return null; - } - var result = await query - .GroupBy(o => o.IdTelemetry) - .Select(g => new DatesRangeDto - { - From = g.Min(o => o.DateStart).DateTime, - To = g.Max(o => o.DateEnd).DateTime - }) - .FirstOrDefaultAsync(token); - return result; - } - - private IQueryable BuildQuery(SubsystemOperationTimeRequest request) - { - var well = wellService.GetOrDefault(request.IdWell) - ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Not valid IdWell = {request.IdWell}"); - - var query = db.SubsystemOperationTimes - .Include(o => o.Subsystem) - .Where(o => o.IdTelemetry == well.IdTelemetry) - .AsNoTracking(); - - if (request.IdsSubsystems.Any()) - query = query.Where(o => request.IdsSubsystems.Contains(o.IdSubsystem)); - - // # Dates range condition - // [GtDate LtDate] - // [DateStart DateEnd] [DateStart DateEnd] - if (request.GtDate.HasValue) - { - DateTimeOffset gtDate = request.GtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours); - query = query.Where(o => o.DateEnd >= gtDate); - } - - if (request.LtDate.HasValue) - { - DateTimeOffset ltDate = request.LtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours); - query = query.Where(o => o.DateStart <= ltDate); - } - - if (request.GtDepth.HasValue) - query = query.Where(o => o.DepthEnd >= request.GtDepth.Value); - - if (request.LtDepth.HasValue) - query = query.Where(o => o.DepthStart <= request.LtDepth.Value); - - if (request?.SortFields?.Any() == true) - { - query = query.SortBy(request.SortFields); - } - else - { - query = query - .OrderBy(o => o.DateStart) - .ThenBy(o => o.DepthStart); - } - - if (request?.Skip > 0) - query = query.Skip((int)request.Skip); - - if (request?.Take > 0) - query = query.Take((int)request.Take); - - return query; - } - - private static SubsystemOperationTimeDto Convert(SubsystemOperationTime operationTime, double? timezoneHours = null) - { - var dto = operationTime.Adapt(); - var hours = timezoneHours ?? operationTime.Telemetry.TimeZone.Hours; - dto.DateStart = operationTime.DateStart.ToRemoteDateTime(hours); - dto.DateEnd = operationTime.DateEnd.ToRemoteDateTime(hours); - return dto; - } + this.db = db; + this.wellService = wellService; + this.subsystemService = subsystemService; + this.detectedOperationService = detectedOperationService; } + + /// + public async Task DeleteAsync(SubsystemOperationTimeRequest request, CancellationToken token) + { + var well = await wellService.GetOrDefaultAsync(request.IdWell, token) + ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist"); + + var query = BuildQuery(request, well); + db.SubsystemOperationTimes.RemoveRange(query); + return await db.SaveChangesAsync(token); + } + + /// + public async Task> GetOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token) + { + var well = await wellService.GetOrDefaultAsync(request.IdWell, token) + ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist"); + + var dtos = await GetOperationTimeAsync(request, well, token); + return dtos; + } + + private async Task> GetOperationTimeAsync(SubsystemOperationTimeRequest request, WellDto well, CancellationToken token) + { + var query = BuildQuery(request, well); + IEnumerable data = await query.ToListAsync(token); + + if (request.SelectMode == SubsystemOperationTimeRequest.SelectModeInner) + { + if (request.GtDate is not null) + data = data.Where(o => o.DateStart >= request.GtDate.Value); + + if (request.LtDate is not null) + data = data.Where(o => o.DateEnd <= request.LtDate.Value); + } + else if (request.SelectMode == SubsystemOperationTimeRequest.SelectModeTrim) + { + var begin = request.GtDate?.ToUtcDateTimeOffset(well.Timezone.Hours); + var end = request.LtDate?.ToUtcDateTimeOffset(well.Timezone.Hours); + data = TrimOperation(data, begin, end); + } + + var dtos = data.Select(o => Convert(o, well.Timezone.Hours)); + return dtos; + } + + /// + public async Task> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token) + { + var well = await wellService.GetOrDefaultAsync(request.IdWell, token) + ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist"); + + request.SelectMode = SubsystemOperationTimeRequest.SelectModeTrim; + var subsystemsTimes = await GetOperationTimeAsync(request, well, token); + if (subsystemsTimes is null) + return Enumerable.Empty(); + + var detectedOperationSummaryRequest = new DetectedOperationSummaryRequest() + { + IdsTelemetries = new[] {well.IdTelemetry!.Value}, + IdsOperationCategories = WellOperationCategory.MechanicalDrillingSubIds, + + GeDateStart = request.GtDate, + LeDateStart = request.LtDate, + + GeDepthStart = request.GtDepth, + LeDepthStart = request.LtDepth, + }; + var operationsSummaries = await detectedOperationService.GetOperationSummaryAsync(detectedOperationSummaryRequest, token); + if(!operationsSummaries.Any()) + return Enumerable.Empty(); + + var statList = CalcStat(subsystemsTimes, operationsSummaries); + return statList; + } + + private static IEnumerable TrimOperation(IEnumerable data, DateTimeOffset? gtDate, DateTimeOffset? ltDate) + { + if (!ltDate.HasValue && !gtDate.HasValue) + return data.Select(d => d.Adapt()); + + var items = data.Select((item) => + { + var operationTime = item.Adapt(); + if (!(item.DepthStart.HasValue && item.DepthEnd.HasValue)) + return operationTime; + + var dateDiff = (item.DateEnd - item.DateStart).TotalSeconds; + var depthDiff = item.DepthEnd.Value - item.DepthStart.Value; + var a = depthDiff / dateDiff; + var b = item.DepthStart.Value; + + if (gtDate.HasValue && item.DateStart < gtDate.Value) + { + operationTime.DateStart = gtDate.Value; + var x = (gtDate.Value - item.DateStart).TotalSeconds; + operationTime.DepthStart = (float)(a * x + b); + } + if (ltDate.HasValue && item.DateEnd > ltDate.Value) + { + operationTime.DateEnd = ltDate.Value; + var x = (ltDate.Value - item.DateStart).TotalSeconds; + operationTime.DepthEnd = (float)(a * x + b); + } + return operationTime; + }); + + return items; + } + + private IEnumerable CalcStat( + IEnumerable subsystemsTimes, + IEnumerable operationsSummaries) + { + var groupedSubsystemsTimes = subsystemsTimes + .OrderBy(o => o.Id) + .GroupBy(o => o.IdSubsystem); + + var periodGroupTotal = subsystemsTimes.Sum(o => (o.DateEnd - o.DateStart).TotalHours); + + var result = groupedSubsystemsTimes.Select(g => + { + var periodGroup = g.Sum(o => (o.DateEnd - o.DateStart).TotalHours); + var periodGroupDepth = g.Sum(o => o.DepthEnd - o.DepthStart); + var (sumOprationsDepth, sumOprationsDurationHours) = AggregateOperationsSummaries(g.Key, operationsSummaries); + var subsystemStat = new SubsystemStatDto() + { + IdSubsystem = g.Key, + SubsystemName = subsystemService.GetOrDefault(g.Key)?.Name ?? "unknown", + UsedTimeHours = periodGroup, + SumOperationDepthInterval = sumOprationsDepth, + SumOperationDurationHours = sumOprationsDurationHours, + SumDepthInterval = periodGroupDepth, + KUsage = periodGroupDepth / sumOprationsDepth, + OperationCount = g.Count(), + }; + if (subsystemStat.KUsage > 1) + subsystemStat.KUsage = 1; + return subsystemStat; + }); + + var apdParts = result.Where(x => x.IdSubsystem == 11 || x.IdSubsystem == 12); + if (apdParts.Any()) + { + var apdSum = new SubsystemStatDto() + { + IdSubsystem = IdSubsystemAKB, + SubsystemName = "АПД", + UsedTimeHours = apdParts.Sum(part => part.UsedTimeHours), + SumOperationDepthInterval = apdParts.Sum(part => part.SumOperationDepthInterval), + SumOperationDurationHours = apdParts.Sum(part => part.SumOperationDurationHours), + SumDepthInterval = apdParts.Sum(part => part.SumDepthInterval), + OperationCount = apdParts.Sum(part => part.OperationCount), + }; + apdSum.KUsage = apdSum.SumDepthInterval / apdSum.SumOperationDepthInterval; + if (apdSum.KUsage > 1) + apdSum.KUsage = 1; + result = result.Append(apdSum).OrderBy(m => m.IdSubsystem); + } + + return result; + } + + private static (double SumDepth, double SumDurationHours) AggregateOperationsSummaries(int idSubsystem, IEnumerable operationsSummaries) + => idSubsystem switch + { + IdSubsystemAKBRotor or IdSubsystemTorque => CalcOperationSummariesByCategories(operationsSummaries, WellOperationCategory.IdRotor), + IdSubsystemAKBSlide or IdSubsystemSpin => CalcOperationSummariesByCategories(operationsSummaries, WellOperationCategory.IdSlide), + IdSubsystemAKB or IdSubsystemMSE => CalcOperationSummariesByCategories(operationsSummaries, WellOperationCategory.IdRotor, WellOperationCategory.IdSlide), + _ => throw new ArgumentException($"idSubsystem: {idSubsystem} does not supported in this method", nameof(idSubsystem)), + }; + + private static (double SumDepth, double SumDurationHours) CalcOperationSummariesByCategories( + IEnumerable operationsSummaries, + params int[] idsOperationCategories) + { + var filtered = operationsSummaries.Where(sum => idsOperationCategories.Contains(sum.IdCategory)); + var sumDepth = filtered.Sum(summ => summ.SumDepthIntervals); + var sumDurationHours = filtered.Sum(summ => summ.SumDurationHours); + return (sumDepth, sumDurationHours); + } + + /// + public async Task> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token) + { + var activeWells = await wellService.GetAsync(new() { IdCompany = idCompany, IdState = 1 }, token); + var result = await GetStatAsync(activeWells, gtDate, ltDate, token); + return result; + } + + /// + public async Task> GetStatByActiveWells(IEnumerable wellIds, CancellationToken token) + { + var activeWells = await wellService.GetAsync(new() { Ids = wellIds, IdState = 1 }, token); + var result = await GetStatAsync(activeWells, null, null, token); + return result; + } + + private async Task> GetStatAsync(IEnumerable wells, DateTime? gtDate, DateTime? ltDate, CancellationToken token) + { + if (!wells.Any()) + return Enumerable.Empty(); + + var hoursOffset = wells + .FirstOrDefault(well => well.Timezone is not null) + ?.Timezone.Hours + ?? 5d; + + var beginUTC = gtDate.HasValue + ? gtDate.Value.ToUtcDateTimeOffset(hoursOffset) + : db.SubsystemOperationTimes.Min(s => s.DateStart) + .DateTime + .ToUtcDateTimeOffset(hoursOffset); + + var endUTC = ltDate.HasValue + ? ltDate.Value.ToUtcDateTimeOffset(hoursOffset) + : db.SubsystemOperationTimes.Max(s => s.DateEnd) + .DateTime + .ToUtcDateTimeOffset(hoursOffset); + + IEnumerable idsTelemetries = wells + .Where(w => w.IdTelemetry is not null) + .Select(w => w.IdTelemetry!.Value) + .Distinct(); + + var query = db.SubsystemOperationTimes + .Where(o => idsTelemetries.Contains(o.IdTelemetry) && + o.DateStart >= beginUTC && + o.DateEnd <= endUTC) + .AsNoTracking(); + + var subsystemsOperationTime = await query.ToArrayAsync(token); + + var operationSummaries = await detectedOperationService + .GetOperationSummaryAsync(new () + { + IdsTelemetries = idsTelemetries, + IdsOperationCategories = WellOperationCategory.MechanicalDrillingSubIds, + GeDateStart = beginUTC, + LeDateEnd = endUTC, + }, token); + + var result = wells + .Select(well => { + var dtos = subsystemsOperationTime + .Where(s => s.IdTelemetry == well.IdTelemetry) + .Select(s => Convert(s, well.Timezone.Hours)); + + var wellStat = new SubsystemActiveWellStatDto{ Well = well }; + + var telemetryOperationSummaries = operationSummaries.Where(summ => summ.IdTelemetry == well.IdTelemetry); + if (telemetryOperationSummaries.Any()) + { + var subsystemStat = CalcStat(dtos, telemetryOperationSummaries); + if (subsystemStat.Any()) + { + wellStat.SubsystemAKB = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAKB); + wellStat.SubsystemMSE = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemMSE); + wellStat.SubsystemSpinMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemSpin); + wellStat.SubsystemTorqueMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemTorque); + } + } + + return wellStat; + }); + + return result; + } + + /// + public async Task GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token) + { + var well = await wellService.GetOrDefaultAsync(request.IdWell, token) + ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist"); + + var query = BuildQuery(request, well); + if (query is null) + { + return null; + } + var result = await query + .GroupBy(o => o.IdTelemetry) + .Select(g => new DatesRangeDto + { + From = g.Min(o => o.DateStart).DateTime, + To = g.Max(o => o.DateEnd).DateTime + }) + .FirstOrDefaultAsync(token); + return result; + } + + private IQueryable BuildQuery(SubsystemOperationTimeRequest request, WellDto well) + { + var idTelemetry = well.IdTelemetry + ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} has no telemetry"); + + var query = db.SubsystemOperationTimes + .Include(o => o.Subsystem) + .Where(o => o.IdTelemetry == idTelemetry) + .AsNoTracking(); + + if (request.IdsSubsystems.Any()) + query = query.Where(o => request.IdsSubsystems.Contains(o.IdSubsystem)); + + // # Dates range condition + // [GtDate LtDate] + // [DateStart DateEnd] [DateStart DateEnd] + if (request.GtDate.HasValue) + { + DateTimeOffset gtDate = request.GtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours); + query = query.Where(o => o.DateEnd >= gtDate); + } + + if (request.LtDate.HasValue) + { + DateTimeOffset ltDate = request.LtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours); + query = query.Where(o => o.DateStart <= ltDate); + } + + if (request.GtDepth.HasValue) + query = query.Where(o => o.DepthEnd >= request.GtDepth.Value); + + if (request.LtDepth.HasValue) + query = query.Where(o => o.DepthStart <= request.LtDepth.Value); + + if (request?.SortFields?.Any() == true) + { + query = query.SortBy(request.SortFields); + } + else + { + query = query + .OrderBy(o => o.DateStart) + .ThenBy(o => o.DepthStart); + } + + if (request?.Skip > 0) + query = query.Skip((int)request.Skip); + + if (request?.Take > 0) + query = query.Take((int)request.Take); + + return query; + } + + private static SubsystemOperationTimeDto Convert(SubsystemOperationTime operationTime, double? timezoneHours = null) + { + var dto = operationTime.Adapt(); + var hours = timezoneHours ?? operationTime.Telemetry.TimeZone.Hours; + dto.DateStart = operationTime.DateStart.ToRemoteDateTime(hours); + dto.DateEnd = operationTime.DateEnd.ToRemoteDateTime(hours); + return dto; + } }