DD.WellWorkover.Cloud/AsbCloudInfrastructure/Repository/WellOperationRepository.cs

410 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb;
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<WellOperationDto, WellOperation>,
IWellOperationRepository
{
private readonly IMemoryCache memoryCache;
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
private readonly IWellService wellService;
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 IEnumerable<WellSectionTypeDto> GetSectionTypes() =>
memoryCache
.GetOrCreateBasic(dbContext.WellSectionTypes)
.OrderBy(s => s.Order)
.Select(s => s.Adapt<WellSectionTypeDto>());
public async Task<IEnumerable<WellOperationDto>> GetAsync(WellOperationRequest request, CancellationToken token)
{
var (items, _) = await GetPrivateAsync(request, token);
return items;
}
public async Task<PaginationContainer<WellOperationDto>> GetPageAsync(WellOperationRequest request, CancellationToken token)
{
var skip = request.Skip ?? 0;
var take = request.Take ?? 32;
var (items, count) = await GetPrivateAsync(request, token);
var paginationContainer = new PaginationContainer<WellOperationDto>
{
Skip = skip,
Take = take,
Count = count,
Items = items
};
return paginationContainer;
}
public async Task<IEnumerable<WellGroupOpertionDto>> GetGroupOperationsStatAsync(WellOperationRequest request, CancellationToken token)
{
var query = BuildQuery(request, token);
var entities = (await query)
.Select(o => new
{
o.IdCategory,
DurationMinutes = o.DurationHours * 60,
DurationDepth = o.DepthEnd - o.DepthStart
})
.ToArray();
var parentRelationDictionary = wellOperationCategoryRepository.Get(true)
.ToDictionary(c => c.Id, c => new
{
c.Name,
c.IdParent
});
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
});
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;
});
}
return dtos;
}
public async Task<int> InsertRangeAsync(IEnumerable<WellOperationDto> dtos,
bool deleteBeforeInsert,
CancellationToken token)
{
EnsureValidWellOperations(dtos);
if (!deleteBeforeInsert)
return await InsertRangeAsync(dtos, token);
var idType = dtos.First().IdType;
var idWell = dtos.First().IdWell;
var existingOperationIds = await dbContext.WellOperations
.Where(e => e.IdWell == idWell && e.IdType == idType)
.Select(e => e.Id)
.ToArrayAsync(token);
await DeleteRangeAsync(existingOperationIds, token);
return await InsertRangeAsync(dtos, token);
}
public override Task<int> UpdateRangeAsync(IEnumerable<WellOperationDto> dtos, CancellationToken token)
{
EnsureValidWellOperations(dtos);
return base.UpdateRangeAsync(dtos, token);
}
private static void EnsureValidWellOperations(IEnumerable<WellOperationDto> dtos)
{
if (dtos.GroupBy(d => d.IdType).Count() > 1)
throw new ArgumentInvalidException(nameof(dtos), "Все операции должны быть одного типа");
if (dtos.GroupBy(d => d.IdType).Count() > 1)
throw new ArgumentInvalidException(nameof(dtos), "Все операции должны принадлежать одной скважине");
}
private async Task<IEnumerable<WellOperation>> GetByIdsWells(IEnumerable<int> idsWells, CancellationToken token)
{
var query = GetQuery()
.Where(e => idsWells.Contains(e.IdWell))
.OrderBy(e => e.DateStart);
var entities = await query.ToArrayAsync(token);
return entities;
}
private async Task<(IEnumerable<WellOperationDto> items, int count)> GetPrivateAsync(WellOperationRequest request, CancellationToken token)
{
var skip = request.Skip ?? 0;
var take = request.Take ?? 32;
/*
каунт = сумма всех фильтеред1
for{
все записи по скважине и типу план/факт = wellOperationswithType
из wellOperationswithType выбираем первую операцию
из wellOperationswithType все НПВ
к wellOperationswithType применяем оставшиеся фильтры из buildquery = фильтеред1
к фильтеред1 применить скип тэйк = фильтеред2
фильтеред2 конвертировать в дто и рассчитать дэй и время нпв
...
}
*/
var entities = await GetByIdsWells(request.IdsWell, token);
var entitiesByWellAndType = entities
.GroupBy(e => new { e.IdWell, e.IdType })
.Select(grp => grp.ToArray());
var result = new List<WellOperationDto>();
foreach (var wellOperationswithType in entitiesByWellAndType)
{
var firstWellOperation = wellOperationswithType
.OrderBy(e => e.DateStart)
.FirstOrDefault();
//НПВ
var operationsWithNpt = wellOperationswithType
.Where(o => WellOperationCategory.NonProductiveTimeSubIds.Contains(o.IdCategory));
var filteredWellOperations = FilterOperations(wellOperationswithType, request);
var filteredWellOperationsPart = filteredWellOperations
.Skip(skip)
.Take(take);
var dtos = filteredWellOperationsPart
.Select(o => ConvertWithDrillingDaysAndNpvHours(o, firstWellOperation, operationsWithNpt, token));
result.AddRange(dtos);
}
return (result, entities.Count());
}
private IEnumerable<WellOperation> FilterOperations(IEnumerable<WellOperation> 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.GeDate.HasValue)
{
var geDateUtc = request.GeDate.Value.UtcDateTime;
entities = entities.Where(e => e.DateStart >= geDateUtc);
}
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);
return entities;
}
private async Task<IEnumerable<WellOperation>> BuildQuery(WellOperationRequest request, CancellationToken token)
{
var entities = await GetByIdsWells(request.IdsWell, token);
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.GeDate.HasValue)
{
var geDateUtc = request.GeDate.Value.UtcDateTime;
entities = entities.Where(e => e.DateStart >= geDateUtc);
}
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);
return entities;
}
public async Task<IEnumerable<SectionByOperationsDto>> GetSectionsAsync(IEnumerable<int> idsWells, CancellationToken token)
{
const string keyCacheSections = "OperationsBySectionSummarties";
var cache = await memoryCache.GetOrCreateAsync(keyCacheSections, async (entry) =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);
var query = dbContext.Set<WellOperation>()
.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,
First = group
.OrderBy(operation => operation.DateStart)
.Select(operation => new
{
operation.DateStart,
operation.DepthStart,
})
.First(),
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,
Caption = item.Caption,
DateStart = item.First.DateStart,
DepthStart = item.First.DepthStart,
DateEnd = item.Last.DateStart.AddHours(item.Last.DurationHours),
DepthEnd = item.Last.DepthEnd,
})
.ToArray()
.AsEnumerable();
entry.Value = sections;
return sections;
});
return cache;
}
public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken)
{
var query = dbContext.WellOperations.Where(o => o.IdWell == idWell && o.IdType == idType);
if (!await query.AnyAsync(cancellationToken))
return null;
var timeZoneOffset = wellService.GetTimezone(idWell).Offset;
var minDate = await query.MinAsync(o => o.DateStart, cancellationToken);
var maxDate = await query.MaxAsync(o => o.DateStart, cancellationToken);
return new DatesRangeDto
{
From = minDate.ToOffset(timeZoneOffset),
To = maxDate.ToOffset(timeZoneOffset)
};
}
private WellOperationDto ConvertWithDrillingDaysAndNpvHours(
WellOperation entity,
WellOperation firstOperation,
IEnumerable<WellOperation> wellOperationsWithNtp,
CancellationToken token)
{
var dto = Convert(entity);
dto.Day = (entity.DateStart - firstOperation.DateStart).TotalDays;
dto.NptHours = wellOperationsWithNtp
.Where(o => o.DateStart <= entity.DateStart)
.Sum(e => e.DurationHours);
return dto;
}
protected override WellOperation Convert(WellOperationDto src)
{
var entity = src.Adapt<WellOperation>();
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<WellOperationDto>();
dto.DateStart = src.DateStart.ToOffset(timeZoneOffset);
dto.LastUpdateDate = src.LastUpdateDate.ToOffset(timeZoneOffset);
return dto;
}
}