diff --git a/AsbCloudApp/Repositories/IWellOperationCategoryRepository.cs b/AsbCloudApp/Repositories/IWellOperationCategoryRepository.cs
index 4e18c231..ecf915cd 100644
--- a/AsbCloudApp/Repositories/IWellOperationCategoryRepository.cs
+++ b/AsbCloudApp/Repositories/IWellOperationCategoryRepository.cs
@@ -1,5 +1,5 @@
-using AsbCloudApp.Data;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using AsbCloudApp.Data.WellOperation;
namespace AsbCloudApp.Repositories
{
diff --git a/AsbCloudApp/Repositories/IWellOperationRepository.cs b/AsbCloudApp/Repositories/IWellOperationRepository.cs
index dd39ce52..ffc69970 100644
--- a/AsbCloudApp/Repositories/IWellOperationRepository.cs
+++ b/AsbCloudApp/Repositories/IWellOperationRepository.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using AsbCloudApp.Data.WellOperation;
namespace AsbCloudApp.Repositories
{
@@ -17,24 +18,8 @@ namespace AsbCloudApp.Repositories
///
///
IEnumerable GetSectionTypes();
-
- ///
- /// список плановых операций для сопоставления
- ///
- ///
- ///
- ///
- ///
- Task GetOperationsPlanAsync(int idWell, DateTime? currentDate, CancellationToken token);
-
- ///
- /// дата/время первой операции по скважине
- ///
- ///
- ///
- DateTimeOffset? FirstOperationDate(int idWell);
-
- ///
+
+ ///
/// Получить страницу списка операций
///
///
@@ -42,15 +27,7 @@ namespace AsbCloudApp.Repositories
///
Task> GetAsync(WellOperationRequest request, CancellationToken token);
- ///
- /// Получить список операций по запросу
- ///
- ///
- ///
- ///
- Task> GetAsync(WellsOperationRequest request, CancellationToken token);
-
- ///
+ ///
/// Получить страницу списка операций
///
///
@@ -58,39 +35,30 @@ namespace AsbCloudApp.Repositories
///
Task> GetPageAsync(WellOperationRequest request, CancellationToken token);
- ///
- /// Получить операцию по id
- ///
- ///
- ///
- ///
- Task GetOrDefaultAsync(int id, CancellationToken token);
-
- ///
+ ///
/// Получить статистику операции по скважине с группировкой по категориям
///
///
///
///
- Task> GetGroupOperationsStatAsync(
- WellOperationRequest request,
- CancellationToken token);
+ Task> GetGroupOperationsStatAsync(WellOperationRequest request, CancellationToken token);
+
+ ///
+ /// Добавить несколько операций
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task InsertRangeAsync(IEnumerable dtos, bool deleteBeforeInsert, CancellationToken token);
///
- /// Добавить несколько операций за один раз
- ///
- ///
- ///
- ///
- Task InsertRangeAsync(IEnumerable wellOperationDtos, CancellationToken token);
-
- ///
- /// Обновить существующую операцию
+ /// Обновить несколько существующую операций
///
///
///
///
- Task UpdateAsync(WellOperationDto dto, CancellationToken token);
+ Task UpdateRangeAsync(IEnumerable dtos, CancellationToken token);
///
/// Удалить операции по id
@@ -98,7 +66,7 @@ namespace AsbCloudApp.Repositories
///
///
///
- Task DeleteAsync(IEnumerable ids, CancellationToken token);
+ Task DeleteRangeAsync(IEnumerable ids, CancellationToken token);
///
/// Получить секции скважин из операций ГГД. Секцие поделены на плановые и фактические.
@@ -115,24 +83,6 @@ namespace AsbCloudApp.Repositories
///
///
///
- Task GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken);
-
- ///
- /// Удаление полных дубликатов операций по всем скважинам
- ///
- ///
- ///
- ///
- Task RemoveDuplicates(Action onProgressCallback, CancellationToken token);
-
- ///
- /// Усечение пересекающейся последующей операции по дате и глубине забоя
- ///
- /// Фильтр по дате. Если хоть одна операция попадет в в фильтр, то будет обработана вся скважина, а не только эта операция
- /// Фильтр по дате. Если хоть одна операция попадет в в фильтр, то будет обработана вся скважина, а не только эта операция
- ///
- ///
- ///
- Task TrimOverlapping(DateTimeOffset? geDate, DateTimeOffset leDate, Action onProgressCallback, CancellationToken token);
- }
+ Task GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken);
+ }
}
\ No newline at end of file
diff --git a/AsbCloudApp/Requests/WellOperationRequest.cs b/AsbCloudApp/Requests/WellOperationRequest.cs
index 1d190018..3164c2d1 100644
--- a/AsbCloudApp/Requests/WellOperationRequest.cs
+++ b/AsbCloudApp/Requests/WellOperationRequest.cs
@@ -3,110 +3,50 @@ using System.Collections.Generic;
namespace AsbCloudApp.Requests
{
- ///
- /// параметры для запроса списка операций
- ///
- public class WellOperationRequestBase : RequestBase
- {
- ///
- /// фильтр по дате начала операции
- ///
- public DateTime? GeDate { get; set; }
+ public class WellOperationRequest : RequestBase
+ {
+ ///
+ /// Идентификаторы скважин
+ ///
+ public IEnumerable? IdsWell { get; set; }
- ///
- /// фильтр по дате окончания операции
- ///
- public DateTime? LtDate { get; set; }
+ ///
+ /// Больше или равно дате начала операции
+ ///
+ public DateTimeOffset? GeDate { get; set; }
- ///
- /// фильтр. Больше или равно глубины скважины на начало операции.
- ///
- public double? GeDepth { get; set; }
+ ///
+ /// Меньше или равно дате окончания операции
+ ///
+ public DateTimeOffset? LeDate { get; set; }
- ///
- /// фильтр. Меньше или равно глубины скважины на конец операции.
- ///
- public double? LeDepth { get; set; }
+ ///
+ /// Больше или равно глубины скважины на начало операции.
+ ///
+ public double? GeDepth { get; set; }
- ///
- /// фильтр по списку id категорий операции
- ///
- public IEnumerable? OperationCategoryIds { get; set; }
+ ///
+ /// Меньше или равно глубины скважины на конец операции.
+ ///
+ public double? LeDepth { get; set; }
- ///
- /// фильтр по план = 0, факт = 1
- ///
- public int? OperationType { get; set; }
+ ///
+ /// Идентификаторы категорий операции
+ ///
+ public IEnumerable? OperationCategoryIds { get; set; }
- ///
- /// фильтр по списку id конструкций секции
- ///
- public IEnumerable? SectionTypeIds { get; set; }
+ ///
+ /// Тип операций
+ ///
+ /// - 0 - плановая операция
+ /// - 1 - фактическая операция
+ ///
+ ///
+ public int? OperationType { get; set; }
- ///
- /// Параметры для запроса списка операций.
- /// Базовый конструктор
- ///
- public WellOperationRequestBase()
- { }
-
- ///
- /// Параметры для запроса списка операций.
- /// Копирующий конструктор
- ///
- ///
- public WellOperationRequestBase(WellOperationRequestBase request)
- {
- GeDepth = request.GeDepth;
- LeDepth = request.LeDepth;
- GeDate = request.GeDate;
- LtDate = request.LtDate;
-
- OperationCategoryIds = request.OperationCategoryIds;
- OperationType = request.OperationType;
- SectionTypeIds = request.SectionTypeIds;
-
- Skip = request.Skip;
- Take = request.Take;
- SortFields = request.SortFields;
- }
- }
-
- ///
- /// Параметры для запроса списка операций (с id скважины)
- ///
- public class WellOperationRequest : WellOperationRequestBase
- {
- ///
- /// id скважины
- ///
- public int IdWell { get; set; }
-
- ///
- /// ctor
- ///
- public WellOperationRequest() { }
-
- ///
- /// копирующий конструктор
- ///
- ///
- ///
- public WellOperationRequest(WellOperationRequestBase request, int idWell)
- :base(request)
- {
- IdWell = idWell;
- }
- }
-
- ///
- /// Параметры для запроса списка операций (с массивом id скважин)
- ///
- public class WellsOperationRequest : WellOperationRequestBase
- {
- ///
- /// ids скважин
- ///
- public IEnumerable IdsWell { get; set; } = null!;
- }
-}
+ ///
+ /// Идентификаторы конструкций секции
+ ///
+ public IEnumerable? SectionTypeIds { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Services/IDetectedOperationService.cs b/AsbCloudApp/Services/IDetectedOperationService.cs
index 998032ba..208706f7 100644
--- a/AsbCloudApp/Services/IDetectedOperationService.cs
+++ b/AsbCloudApp/Services/IDetectedOperationService.cs
@@ -1,10 +1,10 @@
using System;
-using AsbCloudApp.Data;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Requests;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using AsbCloudApp.Data.WellOperation;
namespace AsbCloudApp.Services
{
diff --git a/AsbCloudApp/Services/IOperationsStatService.cs b/AsbCloudApp/Services/IOperationsStatService.cs
index a92d0553..f5ec7c7c 100644
--- a/AsbCloudApp/Services/IOperationsStatService.cs
+++ b/AsbCloudApp/Services/IOperationsStatService.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using AsbCloudApp.Data.WellOperation;
namespace AsbCloudApp.Services
{
diff --git a/AsbCloudApp/Services/IWellCompositeOperationService.cs b/AsbCloudApp/Services/IWellCompositeOperationService.cs
index 94ba1f93..96dbcf2b 100644
--- a/AsbCloudApp/Services/IWellCompositeOperationService.cs
+++ b/AsbCloudApp/Services/IWellCompositeOperationService.cs
@@ -1,7 +1,7 @@
-using AsbCloudApp.Data;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using AsbCloudApp.Data.WellOperation;
namespace AsbCloudApp.Services
{
@@ -16,6 +16,6 @@ namespace AsbCloudApp.Services
///
///
///
- Task>> GetAsync(IEnumerable idsWells, CancellationToken token);
+ Task>> GetAsync(IEnumerable idsWells, CancellationToken token);
}
}
diff --git a/AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs b/AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs
index f21dd487..3cdce610 100644
--- a/AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs
+++ b/AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs
@@ -114,6 +114,26 @@ namespace AsbCloudInfrastructure.Repository
entry.State = EntityState.Detached;
return entry.Entity.Id;
}
+
+ public virtual async Task UpdateRangeAsync(IEnumerable dtos, CancellationToken token)
+ {
+ if (!dtos.Any())
+ return 0;
+
+ var ids = dtos.Select(d => d.Id);
+
+ var countExistingEntities = await dbSet
+ .Where(d => ids.Contains(d.Id))
+ .CountAsync(token);
+
+ if (ids.Count() > countExistingEntities)
+ return ICrudRepository.ErrorIdNotFound;
+
+ var entities = dtos.Select(Convert);
+ dbContext.Set().UpdateRange(entities);
+
+ return await dbContext.SaveChangesAsync(token);
+ }
///
public virtual Task DeleteAsync(int id, CancellationToken token)
@@ -129,6 +149,14 @@ namespace AsbCloudInfrastructure.Repository
return affected;
}
+ public virtual async Task DeleteRangeAsync(IEnumerable ids, CancellationToken token)
+ {
+ var query = dbContext.Set().Where(e => ids.Contains(e.Id));
+ dbContext.Set().RemoveRange(query);
+
+ return await dbContext.SaveChangesAsync(token);
+ }
+
protected virtual TDto Convert(TEntity src) => src.Adapt();
protected virtual TEntity Convert(TDto src) => src.Adapt();
diff --git a/AsbCloudInfrastructure/Repository/WellOperationCategoryRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationCategoryRepository.cs
index 84da5f3d..927a424e 100644
--- a/AsbCloudInfrastructure/Repository/WellOperationCategoryRepository.cs
+++ b/AsbCloudInfrastructure/Repository/WellOperationCategoryRepository.cs
@@ -1,11 +1,10 @@
-using AsbCloudApp.Data;
-using AsbCloudApp.Repositories;
+using AsbCloudApp.Repositories;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.Extensions.Caching.Memory;
-using System;
using System.Collections.Generic;
using System.Linq;
+using AsbCloudApp.Data.WellOperation;
namespace AsbCloudInfrastructure.Repository;
diff --git a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
index 329d2502..702ca982 100644
--- a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
+++ b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
@@ -1,4 +1,11 @@
-using AsbCloudApp.Data;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using AsbCloudApp.Data;
+using AsbCloudApp.Data.WellOperation;
+using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
@@ -7,693 +14,340 @@ using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository;
-///
-/// репозиторий операций по скважине
-///
-public class WellOperationRepository : IWellOperationRepository
+public class WellOperationRepository : CrudRepositoryBase,
+ IWellOperationRepository
{
- private const string KeyCacheSections = "OperationsBySectionSummarties";
-
- private readonly IAsbCloudDbContext db;
- private readonly IMemoryCache memoryCache;
- private readonly IWellService wellService;
- private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
-
- public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService, IWellOperationCategoryRepository wellOperationCategoryRepository)
- {
- this.db = db;
- this.memoryCache = memoryCache;
- this.wellService = wellService;
- this.wellOperationCategoryRepository = wellOperationCategoryRepository;
- }
-
- public IEnumerable GetSectionTypes() =>
- memoryCache
- .GetOrCreateBasic(db.Set())
- .OrderBy(s => s.Order)
- .Select(s => s.Adapt());
-
- 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 dtos = await BuildQuery(request)
- .AsNoTracking()
- .ToArrayAsync(token);
-
- var dateLastAssosiatedPlanOperation = await GetDateLastAssosiatedPlanOperationAsync(idWell, currentDate, timezone.Hours, token);
-
- var result = new WellOperationPlanDto()
- {
- WellOperationsPlan = dtos.Select(Convert),
- 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 async Task> GetSectionsAsync(IEnumerable idsWells, CancellationToken token)
- {
- var cache = await memoryCache.GetOrCreateAsync(KeyCacheSections, async (entry) =>
- {
- entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);
-
- var query = db.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,
-
- 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(),
- });
- 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;
- });
-
- var sections = cache.Where(s => idsWells.Contains(s.IdWell));
- return sections;
- }
-
- public async Task GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken)
- {
- var timezone = wellService.GetTimezone(idWell);
-
- var query = db.WellOperations.Where(o => o.IdWell == idWell && o.IdType == idType);
-
- if (!await query.AnyAsync(cancellationToken))
- return null;
-
- var minDate = await query.MinAsync(o => o.DateStart, cancellationToken);
- var maxDate = await query.MaxAsync(o => o.DateStart, cancellationToken);
-
- return new DatesRangeDto
- {
- From = minDate.ToRemoteDateTime(timezone.Hours),
- To = maxDate.ToRemoteDateTime(timezone.Hours)
- };
- }
-
- ///
- public DateTimeOffset? FirstOperationDate(int idWell)
- {
- var sections = GetSectionsAsync(new[] { idWell }, CancellationToken.None).Result;
- var first = sections.FirstOrDefault(section => section.IdType == WellOperation.IdOperationTypeFact)
- ?? sections.FirstOrDefault(section => section.IdType == WellOperation.IdOperationTypePlan);
-
- return first?.DateStart;
- }
-
- ///
- public async Task> GetAsync(
- WellOperationRequest request,
- CancellationToken token)
- {
- var query = BuildQuery(request)
- .AsNoTracking();
-
- var dtos = await query.ToArrayAsync(token);
-
- return dtos.Select(Convert);
- }
-
- public async Task> GetAsync(
- WellsOperationRequest request,
- CancellationToken token)
- {
- var query = BuildQuery(request)
- .AsNoTracking();
-
- var dtos = await query.ToArrayAsync(token);
- return dtos;
- }
-
- ///
- public async Task> GetPageAsync(
- WellOperationRequest request,
- CancellationToken token)
- {
- var query = BuildQuery(request);
-
- var result = new PaginationContainer
- {
- Skip = request.Skip ?? 0,
- Take = request.Take ?? 32,
- Count = await query.CountAsync(token),
- };
-
- var dtos = await query.ToArrayAsync(token);
-
- result.Items = dtos.Select(Convert);
-
- 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 = 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 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.DateTime.ToUtcDateTimeOffset(timezone.Hours);
- entity.IdWell = idWell;
- entity.LastUpdateDate = DateTimeOffset.UtcNow;
- db.WellOperations.Add(entity);
- }
-
- var result = await db.SaveChangesAsync(token);
- if (result > 0)
- memoryCache.Remove(KeyCacheSections);
- return result;
-
- }
-
- ///
- public async Task UpdateAsync(
- WellOperationDto dto, CancellationToken token)
- {
- var timezone = wellService.GetTimezone(dto.IdWell);
- var entity = dto.Adapt();
- entity.DateStart = dto.DateStart.DateTime.ToUtcDateTimeOffset(timezone.Hours);
- entity.LastUpdateDate = DateTimeOffset.UtcNow;
- db.WellOperations.Update(entity);
-
- var result = await db.SaveChangesAsync(token);
- if (result > 0)
- memoryCache.Remove(KeyCacheSections);
- return result;
- }
-
- ///
- public async Task DeleteAsync(IEnumerable ids,
- CancellationToken token)
- {
- var query = db.WellOperations.Where(e => ids.Contains(e.Id));
- db.WellOperations.RemoveRange(query);
-
- var result = await db.SaveChangesAsync(token);
- if (result > 0)
- memoryCache.Remove(KeyCacheSections);
- return result;
- }
-
- ///
- /// В результате попрежнему требуется конвертировать дату
- ///
- ///
- ///
- ///
- private IQueryable BuildQuery(WellOperationRequest request)
- {
- var timezone = wellService.GetTimezone(request.IdWell);
- var timeZoneOffset = 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(timeZoneOffset);
- query = query.Where(e => e.DateStart >= geDateOffset);
- }
-
- if (request.LtDate.HasValue)
- {
- var ltDateOffset = request.LtDate.Value.ToUtcDateTimeOffset(timeZoneOffset);
- 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));
-
- // TODO: Вынести query.Select из метода BuildQuery
- var dtos = 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 = o.DateStart,
- 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,
- });
-
- if (request.SortFields?.Any() == true)
- {
- dtos = dtos.SortBy(request.SortFields);
- }
-
- dtos = dtos
- .OrderBy(e => e.DateStart)
- .ThenBy(e => e.DepthEnd)
- .ThenBy(e => e.Id);
-
- if (request.Skip.HasValue)
- dtos = dtos.Skip(request.Skip.Value);
-
- if (request.Take.HasValue)
- dtos = dtos.Take(request.Take.Value);
-
- return dtos.AsNoTracking();
- }
-
- ///
- /// Получение данных по запросу
- ///
- ///
- ///
- ///
- private IQueryable BuildQuery(WellsOperationRequest request)
- {
- var query = db.WellOperations
- .Where(o => request.IdsWell.Contains(o.IdWell))
- .Where(o => request.OperationType == o.IdType);
-
- if (request.SectionTypeIds?.Any() == true)
- query = query.Where(o => request.SectionTypeIds.Contains(o.IdWellSectionType));
-
- if (request.OperationCategoryIds?.Any() == true)
- query = query.Where(o => request.OperationCategoryIds.Contains(o.IdCategory));
-
- // TODO: Вынести query.Select из метода BuildQuery
- var dtos = query.Select(o => new WellOperationDataDto
- {
- DepthStart = o.DepthStart,
- DurationHours = o.DurationHours,
- IdCategory = o.IdCategory,
- IdWell = o.IdWell,
- IdWellSectionType = o.IdWellSectionType,
- OperationCategoryName = o.OperationCategory.Name,
- WellSectionTypeCaption = o.WellSectionType.Caption,
- });
-
- if (request.SortFields?.Any() == true)
- {
- dtos = dtos.SortBy(request.SortFields);
- }
-
- if (request.Skip.HasValue)
- dtos = dtos.Skip(request.Skip.Value);
-
- if (request.Take.HasValue)
- dtos = dtos.Take(request.Take.Value);
-
- return dtos.AsNoTracking();
- }
-
- private WellOperationDto Convert(WellOperationDto dto)
- {
- var timezone = wellService.GetTimezone(dto.IdWell);
- var timezoneOffset = TimeSpan.FromHours(timezone.Hours);
-
- var dtoWithRemoteDateTime = dto.Adapt();
-
- dtoWithRemoteDateTime.DateStart = dto.DateStart.ToOffset(TimeSpan.FromHours(timezoneOffset.Hours));
- dtoWithRemoteDateTime.LastUpdateDate = dto.LastUpdateDate?.ToOffset(TimeSpan.FromHours(timezoneOffset.Hours));
-
- return dtoWithRemoteDateTime;
- }
-
- public async Task RemoveDuplicates(Action onProgressCallback, CancellationToken token)
- {
- IQueryable dbset = db.Set();
- var query = dbset
- .GroupBy(o => new { o.IdWell, o.IdType })
- .Select(g => new { g.Key.IdWell, g.Key.IdType });
-
- var groups = await query
- .ToArrayAsync(token);
-
- var count = groups.Count();
- var i = 0;
- var totalRemoved = 0;
- var total = 0;
- foreach (var group in groups)
- {
- var result = await RemoveDuplicatesInGroup(group.IdWell, group.IdType, token);
- totalRemoved += result.removed;
- total += result.total;
- var percent = i++ / count;
- var message = $"RemoveDuplicates [{i} of {count}] wellId: {group.IdWell}, opType: {group.IdType}, affected: {result.removed} of {result.total}";
- onProgressCallback?.Invoke(message, percent);
- Trace.TraceInformation(message);
- }
- var messageDone = $"RemoveDuplicates done [{i} of {count}] totalAffected: {totalRemoved} of {total}";
- Trace.TraceInformation(messageDone);
- onProgressCallback?.Invoke(messageDone, 1);
- return totalRemoved;
- }
-
- private async Task<(int removed, int total)> RemoveDuplicatesInGroup(int idWell, int idType, CancellationToken token)
- {
- var dbset = db.Set();
- var entities = await dbset
- .Where(o => o.IdWell == idWell && o.IdType == idType)
- .OrderBy(o => o.DateStart)
- .ToListAsync(token);
-
- using var entitiesEnumerator = entities.GetEnumerator();
-
- if (!entitiesEnumerator.MoveNext())
- return (0, 0);
-
- var preEntity = entitiesEnumerator.Current;
- while (entitiesEnumerator.MoveNext())
- {
- var entity = entitiesEnumerator.Current;
- if (preEntity.IsSame(entity))
- dbset.Remove(entity);
- else
- preEntity = entity;
- }
- var removed = await db.SaveChangesAsync(token);
- return (removed, entities.Count);
- }
-
- public async Task TrimOverlapping(DateTimeOffset? geDate, DateTimeOffset leDate, Action onProgressCallback, CancellationToken token)
- {
- var leDateUtc = leDate.ToUniversalTime();
- IQueryable query = db.Set();
- if (geDate.HasValue)
- {
- var geDateUtc = geDate.Value.ToUniversalTime();
- query = query.Where(e => e.DateStart >= geDateUtc);
- }
-
- var groups = await query
- .GroupBy(o => new { o.IdWell, o.IdType })
- .Select(g => new{
- MaxDate = g.Max(o => o.DateStart),
- g.Key.IdWell,
- g.Key.IdType,
- })
- .Where(g => g.MaxDate <= leDateUtc)
- .ToArrayAsync(token);
-
- var count = groups.Count();
- var i = 0;
- (int takeover, int trimmed,int total) totalResult = (0, 0, 0);
- foreach (var group in groups)
- {
- var result = await TrimOverlapping(group.IdWell, group.IdType, token);
- totalResult.takeover += result.takeover;
- totalResult.trimmed += result.trimmed;
- totalResult.total += result.total;
- var percent = i++ / count;
- var message = $"TrimOverlapping [{i} of {count}] wellId: {group.IdWell}, opType: {group.IdType}, takeover:{result.takeover}, trimmed:{result.trimmed}, of {result.total}";
- onProgressCallback?.Invoke(message, percent);
- Trace.TraceInformation(message);
- }
- var messageDone = $"TrimOverlapping done [{i} of {count}] total takeover:{totalResult.takeover}, total trimmed:{totalResult.trimmed} of {totalResult.total}";
- Trace.TraceInformation(messageDone);
- onProgressCallback?.Invoke(messageDone, 1);
- return totalResult.takeover + totalResult.trimmed;
- }
-
- private async Task<(int takeover, int trimmed, int total)> TrimOverlapping(int idWell, int idType, CancellationToken token)
- {
- var dbset = db.Set();
- var query = dbset
- .Where(o => o.IdWell == idWell)
- .Where(o => o.IdType == idType)
- .OrderBy(o => o.DateStart)
- .ThenBy(o => o.DepthStart);
-
- var entities = await query
- .ToListAsync(token);
-
- using var entitiesEnumerator = entities.GetEnumerator();
-
- if (!entitiesEnumerator.MoveNext())
- return (0, 0, 0);
-
- int takeover = 0;
- int trimmed = 0;
- var preEntity = entitiesEnumerator.Current;
- while (entitiesEnumerator.MoveNext())
- {
- var entity = entitiesEnumerator.Current;
- var preDepth = preEntity.DepthEnd;
-
- if (preEntity.DepthEnd >= entity.DepthEnd)
- {
- dbset.Remove(entity);
- takeover++;
- continue;
- }
-
- if (preEntity.DepthEnd > entity.DepthStart)
- {
- entity.DepthStart = preEntity.DepthEnd;
- trimmed++;
- }
-
- var preDate = preEntity.DateStart.AddHours(preEntity.DurationHours);
-
- if (preDate >= entity.DateStart.AddHours(entity.DurationHours))
- {
- dbset.Remove(entity);
- takeover++;
- continue;
- }
-
- if (preDate > entity.DateStart)
- {
- var entityDateEnd = entity.DateStart.AddHours(entity.DurationHours);
- entity.DateStart = preDate;
- entity.DurationHours = (entityDateEnd - entity.DateStart).TotalHours;
- trimmed++;
- }
-
- preEntity = entity;
- }
- var affected = await db.SaveChangesAsync(token);
- return (takeover, trimmed, entities.Count);
- }
-}
+ 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 GetSectionTypes() =>
+ memoryCache
+ .GetOrCreateBasic(dbContext.WellSectionTypes)
+ .OrderBy(s => s.Order)
+ .Select(s => s.Adapt());
+
+ public async Task> GetAsync(WellOperationRequest request, CancellationToken token)
+ {
+ var entities = await BuildQuery(request)
+ .AsNoTracking()
+ .ToArrayAsync(token);
+
+ var dtos = entities.Select(Convert);
+
+ return dtos;
+ }
+
+ public async Task> GetPageAsync(WellOperationRequest request, CancellationToken token)
+ {
+ var skip = request.Skip ?? 0;
+ var take = request.Take ?? 32;
+
+ var query = BuildQuery(request);
+
+ var entites = await query.Skip(skip)
+ .Take(take)
+ .AsNoTracking()
+ .ToArrayAsync(token);
+
+ var paginationContainer = new PaginationContainer
+ {
+ Skip = skip,
+ Take = take,
+ Count = await query.CountAsync(token),
+ Items = entites.Select(Convert)
+ };
+
+ return paginationContainer;
+ }
+
+ 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 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 InsertRangeAsync(IEnumerable 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 UpdateRangeAsync(IEnumerable dtos, CancellationToken token)
+ {
+ EnsureValidWellOperations(dtos);
+
+ return base.UpdateRangeAsync(dtos, token);
+ }
+
+ private static void EnsureValidWellOperations(IEnumerable 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 IQueryable BuildQuery(WellOperationRequest request)
+ {
+ var currentWellOperations = GetQuery()
+ .Where(e => request.IdsWell != null && request.IdsWell.Contains(e.Id));
+
+ var query = GetQuery()
+ .Where(e => request.IdsWell != null && request.IdsWell.Contains(e.Id))
+ .Select(o => new WellOperation
+ {
+ Id = o.Id,
+ IdPlan = o.IdPlan,
+ IdType = o.IdType,
+ IdWell = o.IdWell,
+ LastUpdateDate = o.LastUpdateDate,
+ IdWellSectionType = o.IdWellSectionType,
+ IdCategory = o.IdCategory,
+ OperationCategory = o.OperationCategory,
+ WellSectionType = o.WellSectionType,
+ DateStart = o.DateStart,
+ DepthStart = o.DepthStart,
+ DepthEnd = o.DepthEnd,
+ DurationHours = o.DurationHours,
+ CategoryInfo = o.CategoryInfo,
+ Comment = o.Comment,
+ IdUser = o.IdUser,
+
+ NptHours = currentWellOperations
+ .Where(e => e.IdType == 1 && e.IdWell == o.IdWell)
+ .Where(e => WellOperationCategory.NonProductiveTimeSubIds.Contains(e.IdCategory))
+ .Select(e => e.DurationHours)
+ .Sum(),
+
+ Day = (o.DateStart - currentWellOperations
+ .Where(subOp => subOp.IdType == o.IdType && subOp.IdWell == o.IdWell)
+ .Where(subOp => subOp.DateStart <= o.DateStart)
+ .Min(subOp => subOp.DateStart))
+ .TotalDays
+ });
+
+ if (request.OperationType.HasValue)
+ query = query.Where(e => e.IdType == request.OperationType.Value);
+
+ if (request.SectionTypeIds?.Any() is true)
+ query = query.Where(e => request.SectionTypeIds.Contains(e.IdWellSectionType));
+
+ if (request.OperationCategoryIds?.Any() is 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 geDateUtc = request.GeDate.Value.UtcDateTime;
+ query = query.Where(e => e.DateStart >= geDateUtc);
+ }
+
+ if (request.LeDate.HasValue)
+ {
+ var leDateUtc = request.LeDate.Value.UtcDateTime;
+ query = query.Where(e => e.DateStart <= leDateUtc);
+ }
+
+ if (request.SortFields?.Any() is true)
+ query = query.SortBy(request.SortFields);
+
+ return query;
+ }
+
+ public async Task> GetSectionsAsync(IEnumerable idsWells, CancellationToken token)
+ {
+ const string keyCacheSections = "OperationsBySectionSummarties";
+
+ var cache = await memoryCache.GetOrCreateAsync(keyCacheSections, async (entry) =>
+ {
+ entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);
+
+ 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,
+
+ 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(),
+ });
+ 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;
+ });
+
+ var sections = cache.Where(s => idsWells.Contains(s.IdWell));
+ return sections;
+ }
+
+ public async Task 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 minDate = await query.MinAsync(o => o.DateStart, cancellationToken);
+ var maxDate = await query.MaxAsync(o => o.DateStart, cancellationToken);
+
+ return new DatesRangeDto
+ {
+ From = minDate.ToOffset(minDate.Offset),
+ To = maxDate.ToOffset(minDate.Offset)
+ };
+ }
+
+ protected override WellOperation Convert(WellOperationDto src)
+ {
+ var entity = src.Adapt();
+ entity.LastUpdateDate = src.LastUpdateDate?.UtcDateTime;
+ 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;
+ }
+}
\ No newline at end of file