diff --git a/AsbCloudApp/Repositories/IWellOperationRepository.cs b/AsbCloudApp/Repositories/IWellOperationRepository.cs index 0409969e..d37a1e90 100644 --- a/AsbCloudApp/Repositories/IWellOperationRepository.cs +++ b/AsbCloudApp/Repositories/IWellOperationRepository.cs @@ -54,7 +54,7 @@ namespace AsbCloudApp.Repositories /// /// Обновить существующую операцию /// - /// + /// /// /// Task UpdateRangeAsync(IEnumerable dtos, CancellationToken token); diff --git a/AsbCloudApp/Requests/WellOperationRequest.cs b/AsbCloudApp/Requests/WellOperationRequest.cs index 356ef52d..fa866378 100644 --- a/AsbCloudApp/Requests/WellOperationRequest.cs +++ b/AsbCloudApp/Requests/WellOperationRequest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; namespace AsbCloudApp.Requests; @@ -8,44 +9,72 @@ namespace AsbCloudApp.Requests; /// public class WellOperationRequestBase : RequestBase { - /// - /// Больше или равно дате начала операции - /// - public DateTimeOffset? GeDate { get; set; } + /// + /// Больше или равно дате начала операции + /// + public DateTimeOffset? GeDate { get; set; } - /// - /// Меньше или равно дате окончания операции - /// - public DateTimeOffset? LeDate { get; set; } + /// + /// Меньше или равно дате окончания операции + /// + public DateTimeOffset? LeDate { get; set; } - /// - /// Больше или равно глубины скважины на начало операции. - /// - public double? GeDepth { get; set; } + /// + /// Больше или равно глубины скважины на начало операции. + /// + public double? GeDepth { get; set; } - /// - /// Меньше или равно глубины скважины на конец операции. - /// - public double? LeDepth { get; set; } + /// + /// Меньше или равно глубины скважины на конец операции. + /// + public double? LeDepth { get; set; } - /// - /// Идентификаторы категорий операции - /// - public IEnumerable? OperationCategoryIds { get; set; } + /// + /// Идентификаторы категорий операции + /// + public IEnumerable? OperationCategoryIds { get; set; } - /// - /// Тип операций - /// - /// 0 - плановая операция - /// 1 - фактическая операция - /// - /// - public int? OperationType { get; set; } + /// + /// Тип операций + /// + /// 0 - плановая операция + /// 1 - фактическая операция + /// + /// + public int? OperationType { get; set; } - /// - /// Идентификаторы конструкций секции - /// - public IEnumerable? SectionTypeIds { get; set; } + /// + /// Идентификаторы конструкций секции + /// + public IEnumerable? SectionTypeIds { get; set; } + + /// + /// + /// + public WellOperationRequestBase() + { + + } + + /// + /// + /// + /// + public WellOperationRequestBase(WellOperationRequestBase request) + { + GeDepth = request.GeDepth; + LeDepth = request.LeDepth; + GeDate = request.GeDate; + LeDate = request.LeDate; + + OperationCategoryIds = request.OperationCategoryIds; + OperationType = request.OperationType; + SectionTypeIds = request.SectionTypeIds; + + Skip = request.Skip; + Take = request.Take; + SortFields = request.SortFields; + } } /// @@ -53,32 +82,23 @@ public class WellOperationRequestBase : RequestBase /// public class WellOperationRequest : WellOperationRequestBase { - /// - public WellOperationRequest(IEnumerable idsWell) - { - IdsWell = idsWell; - } + /// + public WellOperationRequest(IEnumerable idsWell) + { + IdsWell = idsWell; + } - /// - public WellOperationRequest(WellOperationRequestBase request, IEnumerable idsWell) - : this(idsWell) - { - GeDepth = request.GeDepth; - LeDepth = request.LeDepth; - GeDate = request.GeDate; - LeDate = request.LeDate; + /// + public WellOperationRequest(WellOperationRequestBase request, IEnumerable idsWell) + : base(request) + { + IdsWell = idsWell; + } - OperationCategoryIds = request.OperationCategoryIds; - OperationType = request.OperationType; - SectionTypeIds = request.SectionTypeIds; - - Skip = request.Skip; - Take = request.Take; - SortFields = request.SortFields; - } - - /// - /// Идентификаторы скважин - /// - public IEnumerable? IdsWell { get; } + /// + /// Идентификаторы скважин + /// + [Required] + [Length(1, 100)] + public IEnumerable IdsWell { get; } } \ No newline at end of file diff --git a/AsbCloudDb/Model/DefaultData/WellOperationCategories.xlsx b/AsbCloudDb/Model/DefaultData/WellOperationCategories.xlsx index 1f41a051..7d257567 100644 Binary files a/AsbCloudDb/Model/DefaultData/WellOperationCategories.xlsx and b/AsbCloudDb/Model/DefaultData/WellOperationCategories.xlsx differ diff --git a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs index 3b67eb54..9bcc718b 100644 --- a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs +++ b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using AsbCloudApp.Data; +using AsbCloudApp.Data; using AsbCloudApp.Data.WellOperation; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; @@ -14,350 +9,367 @@ using AsbCloudDb.Model; using Mapster; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace AsbCloudInfrastructure.Repository; public class WellOperationRepository : CrudRepositoryBase, - IWellOperationRepository + IWellOperationRepository { - private readonly IMemoryCache memoryCache; - private readonly IWellOperationCategoryRepository wellOperationCategoryRepository; - private readonly IWellService wellService; + private readonly IMemoryCache memoryCache; + private readonly IWellOperationCategoryRepository wellOperationCategoryRepository; + private readonly IWellService wellService; + private Lazy> LazyWellCategories { get; } + private Lazy> LazyWellSectionTypes { get; } - public WellOperationRepository(IAsbCloudDbContext context, - IMemoryCache memoryCache, - IWellOperationCategoryRepository wellOperationCategoryRepository, - IWellService wellService) - : base(context, dbSet => dbSet.Include(e => e.WellSectionType) - .Include(e => e.OperationCategory)) - { - this.memoryCache = memoryCache; - this.wellOperationCategoryRepository = wellOperationCategoryRepository; - this.wellService = wellService; - } + public WellOperationRepository(IAsbCloudDbContext context, + IMemoryCache memoryCache, + IWellOperationCategoryRepository wellOperationCategoryRepository, + IWellService wellService) + : base(context, dbSet => dbSet) + { + this.memoryCache = memoryCache; + this.wellOperationCategoryRepository = wellOperationCategoryRepository; + this.wellService = wellService; - public IEnumerable GetSectionTypes() => - memoryCache - .GetOrCreateBasic(dbContext.WellSectionTypes) - .OrderBy(s => s.Order) - .Select(s => s.Adapt()); + LazyWellCategories = new(() => wellOperationCategoryRepository.Get(true, false).ToDictionary(c => c.Id)); + LazyWellSectionTypes = new(() => GetSectionTypes().ToDictionary(c => c.Id)); + } - public async Task> GetAsync(WellOperationRequest request, CancellationToken token) - { - var query = BuildQuery(request); + public IEnumerable GetSectionTypes() => + memoryCache + .GetOrCreateBasic(dbContext.WellSectionTypes) + .OrderBy(s => s.Order) + .Select(s => s.Adapt()); - if (request.Skip.HasValue) - query = query.Skip(request.Skip.Value); + public async Task> GetAsync(WellOperationRequest request, CancellationToken token) + { + var (items, _) = await GetWithDaysAndNpvAsync(request, token); + return items; + } - if (request.Take.HasValue) - query = query.Take(request.Take.Value); + public async Task> GetPageAsync(WellOperationRequest request, CancellationToken token) + { + var skip = request.Skip ?? 0; + var take = request.Take ?? 32; - var entities = await query.AsNoTracking() - .ToArrayAsync(token); + var (items, count) = await GetWithDaysAndNpvAsync(request, token); - return await ConvertWithDrillingDaysAndNpvHoursAsync(entities, token); - } + var paginationContainer = new PaginationContainer + { + Skip = skip, + Take = take, + Count = count, + Items = items + }; - public async Task> GetPageAsync(WellOperationRequest request, CancellationToken token) - { - var skip = request.Skip ?? 0; - var take = request.Take ?? 32; + return paginationContainer; + } - var query = BuildQuery(request); + public async Task> GetGroupOperationsStatAsync(WellOperationRequest request, CancellationToken token) + { + var query = BuildQuery(request); + var entities = await query + .Select(o => new + { + o.IdCategory, + DurationMinutes = o.DurationHours * 60, + DurationDepth = o.DepthEnd - o.DepthStart + }) + .ToArrayAsync(token); - var entities = await query.Skip(skip) - .Take(take) - .AsNoTracking() - .ToArrayAsync(token); + var parentRelationDictionary = wellOperationCategoryRepository.Get(true) + .ToDictionary(c => c.Id, c => new + { + c.Name, + c.IdParent + }); - var paginationContainer = new PaginationContainer - { - Skip = skip, - Take = take, - Count = await query.CountAsync(token), - Items = await ConvertWithDrillingDaysAndNpvHoursAsync(entities, token) - }; + var dtos = entities + .GroupBy(o => o.IdCategory) + .Select(g => new WellGroupOpertionDto + { + IdCategory = g.Key, + Category = parentRelationDictionary[g.Key].Name, + Count = g.Count(), + MinutesAverage = g.Average(o => o.DurationMinutes), + MinutesMin = g.Min(o => o.DurationMinutes), + MinutesMax = g.Max(o => o.DurationMinutes), + TotalMinutes = g.Sum(o => o.DurationMinutes), + DeltaDepth = g.Sum(o => o.DurationDepth), + IdParent = parentRelationDictionary[g.Key].IdParent + }); - return paginationContainer; - } + while (dtos.All(x => x.IdParent != null)) + { + dtos = dtos + .GroupBy(o => o.IdParent!) + .Select(g => + { + var idCategory = g.Key ?? int.MinValue; + var category = parentRelationDictionary.GetValueOrDefault(idCategory); + var newDto = new WellGroupOpertionDto + { + IdCategory = idCategory, + Category = category?.Name ?? "unknown", + Count = g.Sum(o => o.Count), + DeltaDepth = g.Sum(o => o.DeltaDepth), + TotalMinutes = g.Sum(o => o.TotalMinutes), + Items = g.ToList(), + IdParent = category?.IdParent, + }; + return newDto; + }); + } - public async Task> GetGroupOperationsStatAsync(WellOperationRequest request, CancellationToken token) - { - var query = BuildQuery(request); - var entities = await query - .Select(o => new - { - o.IdCategory, - DurationMinutes = o.DurationHours * 60, - DurationDepth = o.DepthEnd - o.DepthStart - }) - .ToArrayAsync(token); + return dtos; + } - var parentRelationDictionary = wellOperationCategoryRepository.Get(true) - .ToDictionary(c => c.Id, c => new - { - c.Name, - c.IdParent - }); + public async Task InsertRangeAsync(IEnumerable dtos, + bool deleteBeforeInsert, + CancellationToken token) + { + EnsureValidWellOperations(dtos); - var dtos = entities - .GroupBy(o => o.IdCategory) - .Select(g => new WellGroupOpertionDto - { - IdCategory = g.Key, - Category = parentRelationDictionary[g.Key].Name, - Count = g.Count(), - MinutesAverage = g.Average(o => o.DurationMinutes), - MinutesMin = g.Min(o => o.DurationMinutes), - MinutesMax = g.Max(o => o.DurationMinutes), - TotalMinutes = g.Sum(o => o.DurationMinutes), - DeltaDepth = g.Sum(o => o.DurationDepth), - IdParent = parentRelationDictionary[g.Key].IdParent - }); + if (!deleteBeforeInsert) + return await InsertRangeAsync(dtos, token); - while (dtos.All(x => x.IdParent != null)) - { - dtos = dtos - .GroupBy(o => o.IdParent!) - .Select(g => - { - var idCategory = g.Key ?? int.MinValue; - var category = parentRelationDictionary.GetValueOrDefault(idCategory); - var newDto = new WellGroupOpertionDto - { - IdCategory = idCategory, - Category = category?.Name ?? "unknown", - Count = g.Sum(o => o.Count), - DeltaDepth = g.Sum(o => o.DeltaDepth), - TotalMinutes = g.Sum(o => o.TotalMinutes), - Items = g.ToList(), - IdParent = category?.IdParent, - }; - return newDto; - }); - } + var idType = dtos.First().IdType; + var idWell = dtos.First().IdWell; - return dtos; - } + var existingOperationIds = await dbContext.WellOperations + .Where(e => e.IdWell == idWell && e.IdType == idType) + .Select(e => e.Id) + .ToArrayAsync(token); - public async Task InsertRangeAsync(IEnumerable dtos, - bool deleteBeforeInsert, - CancellationToken token) - { - EnsureValidWellOperations(dtos); + await DeleteRangeAsync(existingOperationIds, token); - if (!deleteBeforeInsert) - return await InsertRangeAsync(dtos, token); + return await InsertRangeAsync(dtos, token); + } - var idType = dtos.First().IdType; - var idWell = dtos.First().IdWell; + public override Task UpdateRangeAsync(IEnumerable dtos, CancellationToken token) + { + EnsureValidWellOperations(dtos); - var existingOperationIds = await dbContext.WellOperations - .Where(e => e.IdWell == idWell && e.IdType == idType) - .Select(e => e.Id) - .ToArrayAsync(token); + return base.UpdateRangeAsync(dtos, token); + } - await DeleteRangeAsync(existingOperationIds, token); + private static void EnsureValidWellOperations(IEnumerable dtos) + { + if (dtos.GroupBy(d => d.IdType).Count() > 1) + throw new ArgumentInvalidException(nameof(dtos), "Все операции должны быть одного типа"); - return await InsertRangeAsync(dtos, token); - } + if (dtos.GroupBy(d => d.IdType).Count() > 1) + throw new ArgumentInvalidException(nameof(dtos), "Все операции должны принадлежать одной скважине"); + } - public override Task UpdateRangeAsync(IEnumerable dtos, CancellationToken token) - { - EnsureValidWellOperations(dtos); + private async Task> GetByIdsWells(IEnumerable idsWells, CancellationToken token) + { + var query = GetQuery() + .Where(e => idsWells.Contains(e.IdWell)) + .OrderBy(e => e.DateStart); + var entities = await query.ToArrayAsync(token); + return entities; + } - return base.UpdateRangeAsync(dtos, token); - } + private async Task<(IEnumerable items, int count)> GetWithDaysAndNpvAsync(WellOperationRequest request, CancellationToken token) + { + var skip = request.Skip ?? 0; + var take = request.Take ?? 32; - private static void EnsureValidWellOperations(IEnumerable dtos) - { - if (dtos.GroupBy(d => d.IdType).Count() > 1) - throw new ArgumentInvalidException(nameof(dtos), "Все операции должны быть одного типа"); + var entities = await GetByIdsWells(request.IdsWell, token); + var groupedByWellAndType = entities + .GroupBy(e => new { e.IdWell, e.IdType }); - if (dtos.GroupBy(d => d.IdType).Count() > 1) - throw new ArgumentInvalidException(nameof(dtos), "Все операции должны принадлежать одной скважине"); - } + var result = new List(); + var count = 0; + foreach (var wellOperationsWithType in groupedByWellAndType) + { + var firstWellOperation = wellOperationsWithType + .OrderBy(e => e.DateStart) + .FirstOrDefault()!; - private IQueryable BuildQuery(WellOperationRequest request) - { - var query = GetQuery() - .Where(e => request.IdsWell != null && request.IdsWell.Contains(e.IdWell)) - .OrderBy(e => e.DateStart) - .AsQueryable(); + var operationsWithNpt = wellOperationsWithType + .Where(o => WellOperationCategory.NonProductiveTimeSubIds.Contains(o.IdCategory)); - if (request.OperationType.HasValue) - query = query.Where(e => e.IdType == request.OperationType.Value); + IEnumerable filteredWellOperations = FilterByRequest(wellOperationsWithType.AsQueryable(), request); - if (request.SectionTypeIds?.Any() is true) - query = query.Where(e => request.SectionTypeIds.Contains(e.IdWellSectionType)); + var filteredWellOperationsPart = filteredWellOperations + .Skip(skip) + .Take(take); - if (request.OperationCategoryIds?.Any() is true) - query = query.Where(e => request.OperationCategoryIds.Contains(e.IdCategory)); + var dtos = filteredWellOperationsPart + .Select(entity => + { + var dto = Convert(entity); + dto.Day = (entity.DateStart - firstWellOperation.DateStart).TotalDays; + dto.NptHours = operationsWithNpt + .Where(o => o.DateStart <= entity.DateStart) + .Sum(e => e.DurationHours); + return dto; + }); - if (request.GeDepth.HasValue) - query = query.Where(e => e.DepthEnd >= request.GeDepth.Value); + result.AddRange(dtos); + count += filteredWellOperations.Count(); + } - if (request.LeDepth.HasValue) - query = query.Where(e => e.DepthEnd <= request.LeDepth.Value); + return (result, count); + } - if (request.GeDate.HasValue) - { - var geDateUtc = request.GeDate.Value.UtcDateTime; - query = query.Where(e => e.DateStart >= geDateUtc); - } + private static IQueryable FilterByRequest(IQueryable entities, WellOperationRequest request) + { + if (request.OperationType.HasValue) + entities = entities.Where(e => e.IdType == request.OperationType.Value); + if (request.SectionTypeIds?.Any() is true) + entities = entities.Where(e => request.SectionTypeIds.Contains(e.IdWellSectionType)); + if (request.OperationCategoryIds?.Any() is true) + entities = entities.Where(e => request.OperationCategoryIds.Contains(e.IdCategory)); + if (request.GeDepth.HasValue) + entities = entities.Where(e => e.DepthEnd >= request.GeDepth.Value); + if (request.LeDepth.HasValue) + entities = entities.Where(e => e.DepthEnd <= request.LeDepth.Value); - if (request.LeDate.HasValue) - { - var leDateUtc = request.LeDate.Value.UtcDateTime; - query = query.Where(e => e.DateStart <= leDateUtc); - } + if (request.GeDate.HasValue) + { + var geDateUtc = request.GeDate.Value.UtcDateTime; + entities = entities.Where(e => e.DateStart >= geDateUtc); + } - if (request.SortFields?.Any() is true) - query = query.SortBy(request.SortFields); + if (request.LeDate.HasValue) + { + var leDateUtc = request.LeDate.Value.UtcDateTime; + entities = entities.Where(e => e.DateStart <= leDateUtc); + } + if (request.SortFields?.Any() is true) + entities = entities.AsQueryable().SortBy(request.SortFields); + else + entities = entities.AsQueryable().OrderBy(e => e.DateStart); - return query; - } + return entities; + } - public async Task> GetSectionsAsync(IEnumerable idsWells, CancellationToken token) - { - const string keyCacheSections = "OperationsBySectionSummarties"; + private IQueryable BuildQuery(WellOperationRequest request) + { + var query = GetQuery() + .Where(e => request.IdsWell.Contains(e.IdWell)) + .OrderBy(e => e.DateStart) + .AsQueryable(); + query = FilterByRequest(query, request); - var cache = await memoryCache.GetOrCreateAsync(keyCacheSections, async (entry) => - { - entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30); + return query; + } - var query = dbContext.Set() - .GroupBy(operation => new - { - operation.IdWell, - operation.IdType, - operation.IdWellSectionType, - operation.WellSectionType.Caption, - }) - .Select(group => new - { - group.Key.IdWell, - group.Key.IdType, - group.Key.IdWellSectionType, - group.Key.Caption, + public async Task> GetSectionsAsync(IEnumerable idsWells, CancellationToken token) + { + const string keyCacheSections = "OperationsBySectionSummarties"; - First = group - .OrderBy(operation => operation.DateStart) - .Select(operation => new - { - operation.DateStart, - operation.DepthStart, - }) - .First(), + var cache = await memoryCache.GetOrCreateAsync(keyCacheSections, async (entry) => + { + entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30); - Last = group - .OrderByDescending(operation => operation.DateStart) - .Select(operation => new - { - operation.DateStart, - operation.DurationHours, - operation.DepthEnd, - }) - .First(), - }) - .Where(s => idsWells.Contains(s.IdWell)); - var dbData = await query.ToArrayAsync(token); - var sections = dbData.Select( - item => new SectionByOperationsDto - { - IdWell = item.IdWell, - IdType = item.IdType, - IdWellSectionType = item.IdWellSectionType, + var query = dbContext.Set() + .GroupBy(operation => new + { + operation.IdWell, + operation.IdType, + operation.IdWellSectionType, + operation.WellSectionType.Caption, + }) + .Select(group => new + { + group.Key.IdWell, + group.Key.IdType, + group.Key.IdWellSectionType, + group.Key.Caption, - Caption = item.Caption, + First = group + .OrderBy(operation => operation.DateStart) + .Select(operation => new + { + operation.DateStart, + operation.DepthStart, + }) + .First(), - DateStart = item.First.DateStart, - DepthStart = item.First.DepthStart, + Last = group + .OrderByDescending(operation => operation.DateStart) + .Select(operation => new + { + operation.DateStart, + operation.DurationHours, + operation.DepthEnd, + }) + .First(), + }) + .Where(s => idsWells.Contains(s.IdWell)); + var dbData = await query.ToArrayAsync(token); + var sections = dbData.Select( + item => new SectionByOperationsDto + { + IdWell = item.IdWell, + IdType = item.IdType, + IdWellSectionType = item.IdWellSectionType, - DateEnd = item.Last.DateStart.AddHours(item.Last.DurationHours), - DepthEnd = item.Last.DepthEnd, - }) - .ToArray() - .AsEnumerable(); + Caption = item.Caption, - entry.Value = sections; - return sections; - }); + DateStart = item.First.DateStart, + DepthStart = item.First.DepthStart, - return cache; - } + DateEnd = item.Last.DateStart.AddHours(item.Last.DurationHours), + DepthEnd = item.Last.DepthEnd, + }) + .ToArray() + .AsEnumerable(); - public async Task GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken) - { - var query = dbContext.WellOperations.Where(o => o.IdWell == idWell && o.IdType == idType); + entry.Value = sections; + return sections; + }); - if (!await query.AnyAsync(cancellationToken)) - return null; + return cache!; + } - var timeZoneOffset = wellService.GetTimezone(idWell).Offset; - - var minDate = await query.MinAsync(o => o.DateStart, cancellationToken); - var maxDate = await query.MaxAsync(o => o.DateStart, cancellationToken); + public async Task GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken) + { + var query = dbContext.WellOperations.Where(o => o.IdWell == idWell && o.IdType == idType); - return new DatesRangeDto - { - From = minDate.ToOffset(timeZoneOffset), - To = maxDate.ToOffset(timeZoneOffset) - }; - } + if (!await query.AnyAsync(cancellationToken)) + return null; - private async Task> ConvertWithDrillingDaysAndNpvHoursAsync(IEnumerable entities, - CancellationToken token) - { - var idsWell = entities.Select(e => e.IdWell).Distinct(); + var timeZoneOffset = wellService.GetTimezone(idWell).Offset; - var currentWellOperations = GetQuery() - .Where(entity => idsWell.Contains(entity.IdWell)); + var minDate = await query.MinAsync(o => o.DateStart, cancellationToken); + var maxDate = await query.MaxAsync(o => o.DateStart, cancellationToken); - var dateFirstDrillingOperationByIdWell = await currentWellOperations - .Where(entity => entity.IdType == WellOperation.IdOperationTypeFact) - .GroupBy(entity => entity.IdWell) - .ToDictionaryAsync(g => g.Key, g => g.Min(o => o.DateStart), token); + return new DatesRangeDto + { + From = minDate.ToOffset(timeZoneOffset), + To = maxDate.ToOffset(timeZoneOffset) + }; + } - var operationsWithNptByIdWell = await currentWellOperations.Where(entity => - entity.IdType == WellOperation.IdOperationTypeFact && - WellOperationCategory.NonProductiveTimeSubIds.Contains(entity.IdCategory)) - .GroupBy(entity => entity.IdWell) - .ToDictionaryAsync(g => g.Key, g => g.Select(o => o), token); + protected override WellOperation Convert(WellOperationDto src) + { + var entity = src.Adapt(); + entity.DateStart = src.DateStart.UtcDateTime; + return entity; + } - var dtos = entities.Select(entity => - { - var dto = Convert(entity); + protected override WellOperationDto Convert(WellOperation src) + { + //TODO: пока такое получение TimeZone скважины, нужно исправить на Lazy + //Хоть мы и тянем данные из кэша, но от получения TimeZone в этом методе нужно избавиться, пока так + var timeZoneOffset = wellService.GetTimezone(src.IdWell).Offset; - if (dateFirstDrillingOperationByIdWell.TryGetValue(entity.IdWell, out var dateFirstDrillingOperation)) - dto.Day = (entity.DateStart - dateFirstDrillingOperation).TotalDays; + var dto = src.Adapt(); + dto.DateStart = src.DateStart.ToOffset(timeZoneOffset); + dto.LastUpdateDate = src.LastUpdateDate.ToOffset(timeZoneOffset); - if (operationsWithNptByIdWell.TryGetValue(entity.IdWell, out var wellOperationsWithNtp)) - dto.NptHours = wellOperationsWithNtp - .Where(o => o.DateStart <= entity.DateStart) - .Sum(e => e.DurationHours); - - return dto; - }); - - return dtos; - } - - protected override WellOperation Convert(WellOperationDto src) - { - var entity = src.Adapt(); - entity.DateStart = src.DateStart.UtcDateTime; - return entity; - } - - protected override WellOperationDto Convert(WellOperation src) - { - //TODO: пока такое получение TimeZone скважины, нужно исправить на Lazy - //Хоть мы и тянем данные из кэша, но от получения TimeZone в этом методе нужно избавиться, пока так - var timeZoneOffset = wellService.GetTimezone(src.IdWell).Offset; - var dto = src.Adapt(); - dto.DateStart = src.DateStart.ToOffset(timeZoneOffset); - dto.LastUpdateDate = src.LastUpdateDate.ToOffset(timeZoneOffset); - return dto; - } + dto.OperationCategoryName = LazyWellCategories.Value.TryGetValue(src.IdCategory, out WellOperationCategoryDto? category) ? category.Name : string.Empty; + dto.WellSectionTypeCaption = LazyWellSectionTypes.Value.TryGetValue(src.IdWellSectionType, out WellSectionTypeDto? sectionType) ? sectionType.Caption : string.Empty; + return dto; + } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/WellOperations/Templates/WellOperationFactTemplate.xlsx b/AsbCloudInfrastructure/Services/WellOperations/Templates/WellOperationFactTemplate.xlsx index 928ffeb6..12fc2a44 100644 Binary files a/AsbCloudInfrastructure/Services/WellOperations/Templates/WellOperationFactTemplate.xlsx and b/AsbCloudInfrastructure/Services/WellOperations/Templates/WellOperationFactTemplate.xlsx differ diff --git a/AsbCloudInfrastructure/Services/WellOperations/Templates/WellOperationPlanTemplate.xlsx b/AsbCloudInfrastructure/Services/WellOperations/Templates/WellOperationPlanTemplate.xlsx index ffacf2e5..ca2d01c7 100644 Binary files a/AsbCloudInfrastructure/Services/WellOperations/Templates/WellOperationPlanTemplate.xlsx and b/AsbCloudInfrastructure/Services/WellOperations/Templates/WellOperationPlanTemplate.xlsx differ diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/Files/FactWellOperations.xlsx b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/Files/FactWellOperations.xlsx index 45ee8a0b..a7af0814 100644 Binary files a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/Files/FactWellOperations.xlsx and b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/Files/FactWellOperations.xlsx differ diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/Files/PlanWellOperations.xlsx b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/Files/PlanWellOperations.xlsx index 3083297e..d54dd669 100644 Binary files a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/Files/PlanWellOperations.xlsx and b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/Files/PlanWellOperations.xlsx differ diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/WellOperationControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/WellOperationControllerTest.cs index 9d05aea6..117ddae1 100644 --- a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/WellOperationControllerTest.cs +++ b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/WellOperationControllerTest.cs @@ -27,7 +27,7 @@ public class WellOperationControllerTest : BaseIntegrationTest } /// - /// Успешное добавление операций (без предварительной очистки данных) + /// ( ) /// /// [Fact] @@ -46,7 +46,7 @@ public class WellOperationControllerTest : BaseIntegrationTest } /// - /// Успешное добавление операций (с предварительной очисткой данных) + /// ( ) /// /// [Fact] @@ -65,7 +65,7 @@ public class WellOperationControllerTest : BaseIntegrationTest } /// - /// Успешное обновление операций + /// /// /// [Fact] @@ -87,7 +87,7 @@ public class WellOperationControllerTest : BaseIntegrationTest } /// - /// Получение плановых операций + /// /// /// [Fact] @@ -144,7 +144,7 @@ public class WellOperationControllerTest : BaseIntegrationTest IdWellSectionType = 2, IdCategory = WellOperationCategory.IdSlide, IdPlan = null, - CategoryInfo = "Доп.инфо", + CategoryInfo = ".", IdType = idType, DepthStart = 10.0, DepthEnd = 20.0, @@ -201,7 +201,7 @@ public class WellOperationControllerTest : BaseIntegrationTest var stream = responseTemplate.Content; using var workbook = new XLWorkbook(stream); - var sheet = workbook.GetWorksheet("Справочники"); + var sheet = workbook.GetWorksheet(""); var count = sheet.RowsUsed().Count() - 1; @@ -230,21 +230,75 @@ public class WellOperationControllerTest : BaseIntegrationTest Assert.True(notExistedInDb.Count() == 0); } - private static WellOperation CreateWellOperation(int idWell, int idType = WellOperation.IdOperationTypePlan) => - new() - { - IdWell = idWell, - IdWellSectionType = 2, - IdCategory = WellOperationCategory.IdSlide, - IdPlan = null, - CategoryInfo = "Доп.инфо", - LastUpdateDate = new DateTimeOffset(new DateTime(2023, 1, 10)).ToUniversalTime(), - IdType = idType, - DepthStart = 10.0, - DepthEnd = 20.0, - DateStart = new DateTimeOffset(new DateTime(2023, 1, 10), TimeSpan.FromHours(Defaults.Timezone.Hours)).ToUniversalTime(), - DurationHours = 1.0, - Comment = "1", - IdUser = 1, + [Theory] + [InlineData(WellOperation.IdOperationTypePlan)] + [InlineData(WellOperation.IdOperationTypeFact)] + public async Task GetPageOperationsAsyncWithDaysAndNpv_returns_success(int idType) + { + //arrange + const int pageSize = 10; + const int pageIndex = 0; + + var well = await dbContext.Wells.FirstAsync(); + var entity1 = CreateWellOperation(well.Id); + + var entity2 = entity1.Adapt(); + entity2.DateStart = entity2.DateStart.AddDays(1); + entity2.IdCategory = WellOperationCategory.IdEquipmentDrillingRepair; + entity2.DurationHours = 2; + + var entity3 = entity2.Adapt(); + entity3.DateStart = entity3.DateStart.AddDays(1); + entity3.IdCategory = WellOperationCategory.IdEquipmentDrillingRepair; + entity3.DurationHours = 3; + + dbContext.WellOperations.Add(entity1); + dbContext.WellOperations.Add(entity2); + dbContext.WellOperations.Add(entity3); + + await dbContext.SaveChangesAsync(); + + var request = new WellOperationRequestBase + { + OperationType = WellOperation.IdOperationTypePlan, + Skip = pageIndex, + Take = pageSize, + SortFields = [nameof(WellOperation.DateStart)] }; + + //act + var response = await client.GetPageOperationsAsync(well.Id, request); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + + var items = response.Content.Items.ToArray(); + + Assert.Equal(0, items[0].Day); + Assert.Equal(1, items[1].Day); + Assert.Equal(2, items[2].Day); + + Assert.Equal(0, items[0].NptHours); + Assert.Equal(2, items[1].NptHours); + Assert.Equal(5, items[2].NptHours); + } + + private static WellOperation CreateWellOperation(int idWell, int idType = WellOperation.IdOperationTypePlan) => + new() + { + IdWell = idWell, + IdWellSectionType = 2, + IdCategory = WellOperationCategory.IdSlide, + IdPlan = null, + CategoryInfo = ".", + LastUpdateDate = new DateTimeOffset(new DateTime(2023, 1, 10)).ToUniversalTime(), + IdType = idType, + DepthStart = 10.0, + DepthEnd = 20.0, + DateStart = new DateTimeOffset(new DateTime(2023, 1, 10), TimeSpan.FromHours(Defaults.Timezone.Hours)).ToUniversalTime(), + DurationHours = 1.0, + Comment = "1", + IdUser = 1, + }; } \ No newline at end of file