From d4417a555289445a59059261e99bbf54e9d67f31 Mon Sep 17 00:00:00 2001 From: Olga Nemt Date: Mon, 2 Oct 2023 17:53:34 +0500 Subject: [PATCH 1/7] =?UTF-8?q?=D0=90=D0=BD=D0=B0=D0=BB=D0=B8=D1=82=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BF=D0=BE=20=D1=83=D0=B4=D0=B5=D1=80=D0=B6?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=8E=20=D0=B2=20=D0=BA=D0=BB=D0=B8=D0=BD?= =?UTF-8?q?=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 2/7] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80?= =?UTF-8?q?=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 3/7] =?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 4/7] =?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 5/7] =?UTF-8?q?ISlipsStatsRepository=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B8=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=B2?= =?UTF-8?q?=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 6/7] =?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 7/7] =?UTF-8?q?#15287262=20=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0?= =?UTF-8?q?=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