using AsbCloudApp.Data; 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 { #nullable enable /// /// репозиторий операций по скважине /// public class WellOperationRepository : IWellOperationRepository { private readonly IAsbCloudDbContext db; private readonly IMemoryCache memoryCache; private readonly IWellService wellService; private static Dictionary? firstOperationsCache = null; public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService) { this.db = db; this.memoryCache = memoryCache; this.wellService = wellService; } /// public IEnumerable GetCategories(bool includeParents) { var categories = memoryCache .GetOrCreateBasic(db.Set()); if (!includeParents) { var parentIds = categories .Select(o => o.IdParent) .Distinct(); categories = categories .Where(o => !parentIds.Contains(o.Id)); } var result = categories .OrderBy(o => o.Name) .Adapt>(); return result; } /// public IDictionary GetSectionTypes() => memoryCache .GetOrCreateBasic(db.Set()) .ToDictionary(s => s.Id, s => s.Caption); public async Task GetOperationsPlanAsync(int idWell, DateTime? currentDate, CancellationToken token) { var timezone = wellService.GetTimezone(idWell); var request = new WellOperationRequest() { IdWell = idWell, OperationType = WellOperation.IdOperationTypePlan, }; var entities = await BuildQuery(request) .AsNoTracking() .ToArrayAsync(token) .ConfigureAwait(false); var dateLastAssosiatedPlanOperation = await GetDateLastAssosiatedPlanOperationAsync(idWell, currentDate, timezone.Hours, token); var result = new WellOperationPlanDto() { WellOperationsPlan = entities, DateLastAssosiatedPlanOperation = dateLastAssosiatedPlanOperation }; return result; } private async Task GetDateLastAssosiatedPlanOperationAsync( int idWell, DateTime? lessThenDate, double timeZoneHours, CancellationToken token) { if (lessThenDate is null) return null; var currentDateOffset = lessThenDate.Value.ToUtcDateTimeOffset(timeZoneHours); var timeZoneOffset = TimeSpan.FromHours(timeZoneHours); var lastFactOperation = await db.WellOperations .Where(o => o.IdWell == idWell) .Where(o => o.IdType == WellOperation.IdOperationTypeFact) .Where(o => o.IdPlan != null) .Where(o => o.DateStart < currentDateOffset) .Include(x => x.OperationPlan) .OrderByDescending(x => x.DateStart) .FirstOrDefaultAsync(token) .ConfigureAwait(false); if (lastFactOperation is not null) return DateTime.SpecifyKind(lastFactOperation.OperationPlan.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified); return null; } /// public DateTimeOffset? FirstOperationDate(int idWell) { if (firstOperationsCache is null) { var query = db.WellOperations .GroupBy(o => o.IdWell) .Select(g => new Tuple ( g.Key, g.Where(o => o.IdType == WellOperation.IdOperationTypePlan).Min(o => o.DateStart), g.Where(o => o.IdType == WellOperation.IdOperationTypeFact).Min(o => o.DateStart) )); firstOperationsCache = query .ToDictionary(f => f.Item1, f => f.Item3 ?? f.Item2); } return firstOperationsCache?.GetValueOrDefault(idWell); } /// public async Task> GetAsync( WellOperationRequest request, CancellationToken token) { var query = BuildQuery(request) .AsNoTracking(); var result = await query.ToArrayAsync(token); return result; } /// public async Task> GetPageAsync( WellOperationRequest request, CancellationToken token) { var query = BuildQuery(request) .AsNoTracking(); var result = new PaginationContainer { Skip = request.Skip ?? 0, Take = request.Take ?? 32, Count = await query.CountAsync(token).ConfigureAwait(false), }; query = query .Skip(result.Skip) .Take(result.Take); result.Items = await query.ToListAsync(token); return result; } /// public async Task GetOrDefaultAsync(int id, CancellationToken token) { var entity = await db.WellOperations .Include(s => s.WellSectionType) .Include(s => s.OperationCategory) .FirstOrDefaultAsync(e => e.Id == id, token) .ConfigureAwait(false); if (entity is null) return null; var timezone = wellService.GetTimezone(entity.IdWell); var dto = entity.Adapt(); dto.WellSectionTypeName = entity.WellSectionType.Caption; dto.DateStart = entity.DateStart.ToRemoteDateTime(timezone.Hours); dto.CategoryName = entity.OperationCategory.Name; return dto; } /// public async Task> GetGroupOperationsStatAsync( WellOperationRequest request, CancellationToken token) { // TODO: Rename controller method request.OperationType = WellOperation.IdOperationTypeFact; var query = BuildQuery(request); var entities = await query .Select(o => new { o.IdCategory, DurationMinutes = o.DurationHours * 60, DurationDepth = o.DepthEnd - o.DepthStart }) .ToListAsync(token); var parentRelationDictionary = GetCategories(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 InsertRangeAsync( IEnumerable wellOperationDtos, CancellationToken token) { var firstOperation = wellOperationDtos .FirstOrDefault(); if (firstOperation is null) return 0; var idWell = firstOperation.IdWell; var timezone = wellService.GetTimezone(idWell); foreach (var dto in wellOperationDtos) { var entity = dto.Adapt(); entity.Id = default; entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours); entity.IdWell = idWell; db.WellOperations.Add(entity); } return await db.SaveChangesAsync(token) .ConfigureAwait(false); } /// public async Task UpdateAsync( WellOperationDto dto, CancellationToken token) { var timezone = wellService.GetTimezone(dto.IdWell); var entity = dto.Adapt(); entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours); db.WellOperations.Update(entity); return await db.SaveChangesAsync(token) .ConfigureAwait(false); } /// public async Task DeleteAsync(IEnumerable ids, CancellationToken token) { var query = db.WellOperations.Where(e => ids.Contains(e.Id)); db.WellOperations.RemoveRange(query); return await db.SaveChangesAsync(token) .ConfigureAwait(false); } /// /// В результате попрежнему требуется конвертировать дату /// /// /// private IQueryable BuildQuery(WellOperationRequest request) { var timezone = wellService.GetTimezone(request.IdWell); var timeZoneOffset = TimeSpan.FromHours(timezone.Hours); var query = db.WellOperations .Include(s => s.WellSectionType) .Include(s => s.OperationCategory) .Where(o => o.IdWell == request.IdWell); if (request.OperationType.HasValue) query = query.Where(e => e.IdType == request.OperationType.Value); if (request.SectionTypeIds?.Any() == true) query = query.Where(e => request.SectionTypeIds.Contains(e.IdWellSectionType)); if (request.OperationCategoryIds?.Any() == true) query = query.Where(e => request.OperationCategoryIds.Contains(e.IdCategory)); if (request.GeDepth.HasValue) query = query.Where(e => e.DepthEnd >= request.GeDepth.Value); if (request.LeDepth.HasValue) query = query.Where(e => e.DepthEnd <= request.LeDepth.Value); if (request.GeDate.HasValue) { var geDateOffset = request.GeDate.Value.ToUtcDateTimeOffset(timezone.Hours); query = query.Where(e => e.DateStart >= geDateOffset); } if (request.LtDate.HasValue) { var ltDateOffset = request.LtDate.Value.ToUtcDateTimeOffset(timezone.Hours); query = query.Where(e => e.DateStart < ltDateOffset); } var currentWellOperations = db.WellOperations .Where(subOp => subOp.IdWell == request.IdWell); var wellOperationsWithCategoryNPT = currentWellOperations .Where(subOp => subOp.IdType == 1) .Where(subOp => WellOperationCategory.NonProductiveTimeSubIds.Contains(subOp.IdCategory)); var result = query.Select(o => new WellOperationDto { Id = o.Id, IdPlan = o.IdPlan, IdType = o.IdType, IdWell = o.IdWell, IdWellSectionType = o.IdWellSectionType, IdCategory = o.IdCategory, IdParentCategory = o.OperationCategory.IdParent, CategoryName = o.OperationCategory.Name, WellSectionTypeName = o.WellSectionType.Caption, DateStart = DateTime.SpecifyKind(o.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified), DepthStart = o.DepthStart, DepthEnd = o.DepthEnd, DurationHours = o.DurationHours, CategoryInfo = o.CategoryInfo, Comment = o.Comment, NptHours = wellOperationsWithCategoryNPT .Where(subOp => subOp.DateStart <= o.DateStart) .Select(subOp => subOp.DurationHours) .Sum(), Day = (o.DateStart - currentWellOperations .Where(subOp => subOp.IdType == o.IdType) .Where(subOp => subOp.DateStart <= o.DateStart) .Min(subOp => subOp.DateStart)) .TotalDays, IdUser = o.IdUser, LastUpdateDate = o.LastUpdateDate.ToOffset(TimeSpan.FromHours(timezone.Hours)) }); if (request.SortFields?.Any() == true) { result = result.SortBy(request.SortFields); } else { result = result .OrderBy(e => e.DateStart) .ThenBy(e => e.DepthEnd) .ThenBy(e => e.Id); }; return result; } } #nullable disable }