Add DetectedOperation repository

This commit is contained in:
ngfrolov 2024-02-08 11:38:25 +05:00
parent b263f7973b
commit b7dcf313a2
Signed by untrusted user who does not match committer: ng.frolov
GPG Key ID: E99907A0357B29A7
21 changed files with 708 additions and 439 deletions

View File

@ -1,21 +1,17 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Data.DetectedOperation
namespace AsbCloudApp.Data.DetectedOperation;
/// <summary>
/// Автоматически определенная операция
/// </summary>
public class DetectedOperationDto: IId
{
/// <summary>
/// Автоматически определяемая операция
/// </summary>
public class DetectedOperationDto : IId, IWellRelated
{
/// <inheritdoc/>
[Required]
public int Id { get; set; }
/// <inheritdoc/>
[Required]
public int IdWell { get; set; }
/// <summary>
/// Id телеметрии
/// </summary>
@ -29,28 +25,28 @@ namespace AsbCloudApp.Data.DetectedOperation
public int IdCategory { get; set; }
/// <summary>
/// Id пользователя панели
/// Id пользователя панели на момент начала операции
/// </summary>
[Required]
public int IdUsersAtStart { get; set; }
/// <summary>
/// Дата начала операции в часовом поясе скважины
/// </summary>
[Required]
public DateTime DateStart { get; set; }
public int IdUserAtStart { get; set; }
/// <summary>
/// Дата завершения операции в часовом поясе скважины
/// </summary>
[Required]
public DateTime DateEnd { get; set; }
public DateTimeOffset DateEnd { get; set; }
/// <summary>
/// Продолжительность операции в минутах
/// Дата начала операции в часовом поясе скважины
/// </summary>
[Required]
public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;
public DateTimeOffset DateStart { get; set; }
/// <summary>
/// глубина на завершения операции, м
/// </summary>
[Required]
public double DepthEnd { get; set; }
/// <summary>
/// глубина на начало операции, м
@ -59,10 +55,16 @@ namespace AsbCloudApp.Data.DetectedOperation
public double DepthStart { get; set; }
/// <summary>
/// глубина на завершения операции, м
/// Продолжительность операции в минутах
/// </summary>
[Required]
public double DepthEnd { get; set; }
public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;
/// <summary>
/// Флаг включенной подсистемы
/// </summary>
[Required]
public int EnabledSubsystems { get; set; }
/// <summary>
/// название/описание операции
@ -75,26 +77,9 @@ namespace AsbCloudApp.Data.DetectedOperation
/// </summary>
public string? TelemetryUserName { get; set; }
/// <summary>
/// Бурильщик
/// </summary>
public DrillerDto? Driller { get; set; }
/// <summary>
/// Целевые/нормативные показатели
/// </summary>
public OperationValueDto? OperationValue { get; set; }
/// <summary>
/// Ключевой параметр операции
/// </summary>
[Required]
public double Value { get; set; }
/// <summary>
/// Флаг включенной подсистемы
/// </summary>
[Required]
public int EnabledSubsystems { get; set; }
}
}

View File

@ -13,7 +13,7 @@ namespace AsbCloudApp.Data.DetectedOperation
/// Список всех операций
/// </summary>
[Required]
public IEnumerable<DetectedOperationDto> Operations { get; set; } = Enumerable.Empty<DetectedOperationDto>();
public IEnumerable<DetectedOperationWithDrillerDto> Operations { get; set; } = Enumerable.Empty<DetectedOperationWithDrillerDto>();
/// <summary>
/// Статистика по бурильщикам

View File

@ -1,3 +1,5 @@
using System;
namespace AsbCloudApp.Data
{
/// <summary>
@ -20,6 +22,11 @@ namespace AsbCloudApp.Data
/// </summary>
public bool IsOverride { get; set; }
/// <summary>
/// Ñìåùåíèå ÷àñîâîãî ïîÿñà
/// </summary>
public TimeSpan Offset { get => TimeSpan.FromHours(Hours); }
/// <inheritdoc/>
public override bool Equals(object? obj)
{

View File

@ -86,6 +86,14 @@ namespace AsbCloudApp.Data
second = fullDate.Second;
}
/// <inheritdoc/>
public TimeDto(DateTimeOffset fullDate)
{
hour = fullDate.Hour;
minute = fullDate.Minute;
second = fullDate.Second;
}
/// <summary>
/// Makes System.TimeOnly
/// </summary>

View File

@ -7,7 +7,7 @@ using System.Threading;
namespace AsbCloudApp.Repositories;
/// <summary>
/// Таблица автоопределенных операций
/// Таблица автоматически определенных операций
/// </summary>
public interface IDetectedOperationRepository
{
@ -18,15 +18,15 @@ public interface IDetectedOperationRepository
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> Insert(int idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
Task<int> Insert(int? idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
/// <summary>
/// Получить автоматически определенные по телеметрии операции
/// Получить автоматически определенные операции по телеметрии
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<DetectedOperationDto>> Get(DetectedOperationRequest request, CancellationToken token);
Task<IEnumerable<DetectedOperationDto>> Get(DetectedOperationByTelemetryRequest request, CancellationToken token);
/// <summary>
/// Редактирование записей
@ -49,10 +49,11 @@ public interface IDetectedOperationRepository
/// <summary>
/// Удалить операции
/// </summary>
/// <param name="idUser"></param>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteAsync(int idUser, DetectedOperationRequest request, CancellationToken token);
Task<int> Delete(int idUser, DetectedOperationByTelemetryRequest request, CancellationToken token);
/// <summary>
/// Удаление записей

View File

@ -0,0 +1,17 @@
using AsbCloudApp.Data;
using System.Collections.Generic;
namespace AsbCloudApp.Repositories
{
/// <summary>
/// сервис операций по скважине
/// </summary>
public interface IWellOperationCategoryRepository
{
/// <summary>
/// список названий операций
/// </summary>
/// <returns></returns>
IEnumerable<WellOperationCategoryDto> Get(bool includeParents);
}
}

View File

@ -1,43 +1,74 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace AsbCloudApp.Requests
namespace AsbCloudApp.Requests;
/// <summary>
/// Запрос на получение операций определенных по телеметрии
/// </summary>
public class DetectedOperationByTelemetryRequest : DetectedOperationRequest
{
/// <summary>
///
/// id телеметрии
/// </summary>
public class DetectedOperationByWellRequest : DetectedOperationRequest
{
[Required]
public int IdTelemetry { get; set; }
/// <summary>
/// категория операций
/// Запрос на получение операций определенных по id телеметрии
/// </summary>
public DetectedOperationByTelemetryRequest()
{}
/// <summary>
/// Запрос на получение операций определенных по id телеметрии. Copy
/// </summary>
/// <param name="idTelemetry"></param>
/// <param name="request"></param>
public DetectedOperationByTelemetryRequest(int idTelemetry, DetectedOperationRequest request)
:base(request)
{
IdTelemetry = idTelemetry;
}
}
/// <summary>
/// Запрос на получение операций определенных по id скважины
/// </summary>
public class DetectedOperationByWellRequest : DetectedOperationRequest
{
/// <summary>
/// id скважины
/// </summary>
[Required]
public int IdWell { get; set; }
}
/// <summary>
///
/// Запрос на получение операций определенных по id скважины
/// </summary>
public class DetectedOperationByTelemetryRequest : DetectedOperationRequest
{
/// <summary>
/// Список id телеметрий
/// пустой список - нет фильтрации
/// </summary>
public IEnumerable<int> IdsTelemetries { get; set; } = Array.Empty<int>();
}
public DetectedOperationByWellRequest()
{}
/// <summary>
/// Параметры запроса на получение операций определенных по телеметрии
/// Запрос на получение операций определенных по id скважины. Copy
/// </summary>
public class DetectedOperationRequest : RequestBase
public DetectedOperationByWellRequest(int idWell, DetectedOperationRequest request)
: base(request)
{
IdWell = idWell;
}
}
/// <summary>
/// Запрос на получение операций определенных по телеметрии
/// </summary>
public class DetectedOperationRequest : RequestBase
{
/// <summary>
/// категории операций
/// </summary>
public IEnumerable<int> IdsCategories { get; set; } = Array.Empty<int>();
public IEnumerable<int> IdsCategories { get; set; }
/// <summary>
/// Больше или равно дате
@ -52,17 +83,32 @@ namespace AsbCloudApp.Requests
/// <summary>
/// Больше или равно глубины забоя
/// </summary>
public double? GeDepth { get; set; }
public double? GeDepthStart { get; set; }
/// <summary>
/// Меньше или равно глубины забоя
/// </summary>
public double? LeDepth { get; set; }
public double? LeDepthEnd { get; set; }
/// <summary>
/// Фильтр по пользователю панели
/// Запрос на получение операций определенных по телеметрии
/// </summary>
public int? IdTelemetryUser { get; set; }
public DetectedOperationRequest()
{
IdsCategories = new List<int>();
}
/// <summary>
/// Запрос на получение операций определенных по телеметрии. Copy
/// </summary>
/// <param name="request"></param>
public DetectedOperationRequest(DetectedOperationRequest request)
: base(request)
{
IdsCategories = request.IdsCategories;
GeDateStart = request.GeDateStart;
LeDateEnd = request.LeDateEnd;
GeDepthStart = request.GeDepthStart;
LeDepthEnd = request.LeDepthEnd;
}
}

View File

@ -23,5 +23,23 @@ namespace AsbCloudApp.Requests
/// Указать направление сортировки можно через пробел "asc" или "desc"
/// </summary>
public IEnumerable<string>? SortFields { get; set; }
/// <summary>
/// Базовые параметры запроса
/// </summary>
public RequestBase()
{
}
/// <summary>
/// Базовые параметры запроса. Копирующий конструктор
/// </summary>
/// <param name="request"></param>
public RequestBase(RequestBase request)
{
Skip = request.Skip;
Take = request.Take;
SortFields = request.SortFields;
}
}
}

View File

@ -19,7 +19,7 @@ namespace AsbCloudApp.Services
/// <param name="idWell"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<WellOperationCategoryDto>?> GetCategoriesAsync(int? idWell, CancellationToken token);
Task<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(int? idWell, CancellationToken token);
/// <summary>
/// Получить автоматически определенные по телеметрии операции с анализом по бурильщикам
@ -27,7 +27,7 @@ namespace AsbCloudApp.Services
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<DetectedOperationListDto?> GetAsync(DetectedOperationRequest request, CancellationToken token);
Task<DetectedOperationListDto> GetAsync(DetectedOperationByWellRequest request, CancellationToken token);
/// <summary>
/// Получить автоматически определенные по телеметрии операции
@ -35,7 +35,7 @@ namespace AsbCloudApp.Services
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<DetectedOperationDto>> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token);
Task<IEnumerable<DetectedOperationWithDrillerDto>> GetOperationsAsync(DetectedOperationByWellRequest request, CancellationToken token);
/// <summary>
/// Удалить операции
@ -43,7 +43,7 @@ namespace AsbCloudApp.Services
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteAsync(DetectedOperationRequest request, CancellationToken token);
Task<int> DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token);
/// <summary>
/// Статистика по операциям
@ -51,6 +51,6 @@ namespace AsbCloudApp.Services
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<DetectedOperationStatDto>?> GetOperationsStatAsync(DetectedOperationRequest request, CancellationToken token);
Task<IEnumerable<DetectedOperationStatDto>> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token);
}
}

View File

@ -0,0 +1,33 @@
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using Npgsql;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudDb
{
public static class EFExtensionsExceptionHandling
{
public static async Task<int> SaveChangesWithExceptionHandling(this IAsbCloudDbContext db, CancellationToken token)
{
try
{
var result = await db.SaveChangesAsync(token);
return result;
}
catch (DbUpdateException ex)
{
if (ex.InnerException is PostgresException pgException)
TryConvertPostgresExceptionToValidateException(pgException);
throw;
}
}
private static void TryConvertPostgresExceptionToValidateException(PostgresException pgException)
{
if (pgException.SqlState == PostgresErrorCodes.ForeignKeyViolation)
throw new ArgumentException(pgException.Message + "\r\n" + pgException.Detail, "dtos");
}
}
}

View File

@ -7,7 +7,7 @@ using System.Reflection;
namespace AsbCloudDb
{
public static class EFExtentionsSortBy
public static class EFExtensionsSortBy
{
struct TypeAcessor
{

View File

@ -329,6 +329,8 @@ namespace AsbCloudInfrastructure
services.AddTransient<IProcessMapPlanService<ProcessMapPlanWellReamDto>, ProcessMapPlanService<ProcessMapPlanWellReamDto>>();
services.AddTransient<IWellSectionPlanRepository, WellSectionPlanRepository>();
services.AddTransient<IWellOperationCategoryRepository, WellOperationCategoryRepository>();
services.AddTransient<IDetectedOperationRepository, DetectedOperationRepository>();
services.AddSingleton<ParserServiceFactory>();

View File

@ -0,0 +1,191 @@
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository;
public class DetectedOperationRepository : IDetectedOperationRepository
{
private readonly IAsbCloudDbContext db;
private readonly ITelemetryService telemetryService;
public DetectedOperationRepository(
IAsbCloudDbContext db,
ITelemetryService telemetryService)
{
this.db = db;
this.telemetryService = telemetryService;
}
public async Task<int> Delete(int idUser, DetectedOperationByTelemetryRequest request, CancellationToken token)
{
var query = BuildQuery(request);
db.Set<DetectedOperation>().RemoveRange(query);
return await db.SaveChangesAsync(token);
}
public async Task<int> DeleteRange(int idUser, IEnumerable<int> ids, CancellationToken token)
{
var query = db.Set<DetectedOperation>()
.Where(e => ids.Contains( e.Id));
db.Set<DetectedOperation>()
.RemoveRange(query);
return await db.SaveChangesAsync(token);
}
public async Task<IEnumerable<DetectedOperationDto>> Get(DetectedOperationByTelemetryRequest request, CancellationToken token)
{
var query = BuildQuery(request)
.Include(o => o.OperationCategory);
var entities = await query.ToArrayAsync(token);
var offset = telemetryService.GetTimezone(request.IdTelemetry).Offset;
var dtos = entities.Select(o => Convert(o, offset));
return dtos;
}
public async Task<int> Insert(int? idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
if(!dtos.Any())
return 0;
var entities = dtos.Select(Convert);
var dbset = db.Set<DetectedOperation>();
foreach(var entity in entities)
{
entity.Id = default;
dbset.Add(entity);
}
return await db.SaveChangesWithExceptionHandling(token);
}
public async Task<int> Update(int idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
if (!dtos.Any())
return 0;
var ids = dtos
.Select(o => o.Id)
.Distinct()
.ToArray();
if (ids.Any(id => id == default))
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь Id");
if (ids.Length != dtos.Count())
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь уникальные Id");
var dbSet = db.Set<DetectedOperation>();
var existingEntitiesCount = await dbSet
.Where(o => ids.Contains(o.Id))
.CountAsync(token);
if (ids.Length != existingEntitiesCount)
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны существовать в БД");
var entities = dtos
.Select(Convert)
.ToArray();
var entries = new Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<DetectedOperation>[entities.Length];
for(var i = 0; i < entities.Length; i++)
entries[i] = dbSet.Update(entities[i]);
var result = await db.SaveChangesWithExceptionHandling(token);
for (var i = 0; i < entries.Length; i++)
entries[i].State = EntityState.Detached;
return result;
}
public async Task<int> UpdateOrInsert(int idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
var result = 0;
var itemsToInsert = dtos.Where(e => e.Id == 0);
if (itemsToInsert.Any())
result += await Insert(idUser, itemsToInsert, token);
var itemsToUpdate = dtos.Where(e => e.Id != 0);
if (itemsToUpdate.Any())
result += await Update(idUser, itemsToUpdate, token);
return result;
}
private IQueryable<DetectedOperation> BuildQuery(DetectedOperationByTelemetryRequest request)
{
var query = db.Set<DetectedOperation>()
.Where(o => o.IdTelemetry == request.IdTelemetry);
if (request.IdsCategories.Any())
query = query.Where(o => request.IdsCategories.Contains(o.IdCategory));
if (request.GeDepthStart is not null)
query = query.Where(o => o.DepthStart >= request.GeDepthStart);
if (request.LeDepthEnd is not null)
query = query.Where(o => o.DepthEnd <= request.LeDepthEnd);
if (request.GeDateStart is not null)
{
var geDate = request.GeDateStart.Value.ToUniversalTime();
query = query.Where(o => o.DateStart >= geDate);
}
if (request.LeDateEnd is not null)
{
var leDate = request.LeDateEnd.Value.ToUniversalTime();
query = query.Where(o => o.DateEnd <= leDate);
}
if (request.SortFields?.Any() == true)
{
query = query.SortBy(request.SortFields);
}
else
query = query
.OrderBy(o => o.DateStart)
.ThenBy(o => o.DepthStart);
if (request.Skip.HasValue)
query = query.Skip((int)request.Skip);
if (request.Take.HasValue)
query = query.Take((int)request.Take);
return query;
}
private static DetectedOperationDto Convert(DetectedOperation entity, TimeSpan offset)
{
var dto = entity.Adapt<DetectedOperationDto>();
dto.DateStart = entity.DateStart.ToOffset(offset);
dto.DateEnd = entity.DateEnd.ToOffset(offset);
return dto;
}
private static DetectedOperation Convert(DetectedOperationDto dto)
{
var entity = dto.Adapt<DetectedOperation>();
entity.DateStart = dto.DateStart.ToUniversalTime();
entity.DateEnd = dto.DateEnd.ToUniversalTime();
return entity;
}
}

View File

@ -0,0 +1,44 @@
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
namespace AsbCloudInfrastructure.Repository;
public class WellOperationCategoryRepository : IWellOperationCategoryRepository
{
private readonly IAsbCloudDbContext db;
private readonly IMemoryCache memoryCache;
public WellOperationCategoryRepository(IAsbCloudDbContext db, IMemoryCache memoryCache)
{
this.db = db;
this.memoryCache = memoryCache;
}
public IEnumerable<WellOperationCategoryDto> Get(bool includeParents)
{
var categories = memoryCache
.GetOrCreateBasic(db.Set<WellOperationCategory>());
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<IEnumerable<WellOperationCategoryDto>>();
return result;
}
}

View File

@ -28,35 +28,20 @@ public class WellOperationRepository : IWellOperationRepository
private readonly IAsbCloudDbContext db;
private readonly IMemoryCache memoryCache;
private readonly IWellService wellService;
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService)
public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService, IWellOperationCategoryRepository wellOperationCategoryRepository)
{
this.db = db;
this.memoryCache = memoryCache;
this.wellService = wellService;
this.wellOperationCategoryRepository = wellOperationCategoryRepository;
}
/// <inheritdoc/>
public IEnumerable<WellOperationCategoryDto> GetCategories(bool includeParents)
{
var categories = memoryCache
.GetOrCreateBasic(db.Set<WellOperationCategory>());
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<IEnumerable<WellOperationCategoryDto>>();
return result;
return wellOperationCategoryRepository.Get(includeParents);
}
/// <inheritdoc/>

View File

@ -282,7 +282,7 @@ public class DailyReportService : IDailyReportService
var leDateEnd = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
dailyReport.TimeBalanceBlock.WellOperationSlipsTimeCount = (await detectedOperationService.GetAsync(
new DetectedOperationRequest
new DetectedOperationByWellRequest
{
IdsCategories = new[] { idWellOperationSlipsTime },
IdWell = dailyReport.IdWell,

View File

@ -1,5 +1,6 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb;
@ -11,30 +12,35 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.DetectOperations
{
namespace AsbCloudInfrastructure.Services.DetectOperations;
public class DetectedOperationService : IDetectedOperationService
{
private readonly IAsbCloudDbContext db;
public class DetectedOperationService : IDetectedOperationService
{
private readonly IDetectedOperationRepository operationRepository;
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
private readonly IWellService wellService;
private readonly IRepositoryWellRelated<OperationValueDto> operationValueService;
private readonly IScheduleRepository scheduleService;
public DetectedOperationService(IAsbCloudDbContext db, IWellService wellService,
IRepositoryWellRelated<OperationValueDto> operationValueService, IScheduleRepository scheduleService)
public DetectedOperationService(
IDetectedOperationRepository operationRepository,
IWellOperationCategoryRepository wellOperationCategoryRepository,
IWellService wellService,
IRepositoryWellRelated<OperationValueDto> operationValueRepository,
IScheduleRepository scheduleRepository)
{
this.db = db;
this.operationRepository = operationRepository;
this.wellOperationCategoryRepository = wellOperationCategoryRepository;
this.wellService = wellService;
this.operationValueService = operationValueService;
this.scheduleService = scheduleService;
this.operationValueService = operationValueRepository;
this.scheduleService = scheduleRepository;
}
public async Task<DetectedOperationListDto?> GetAsync(DetectedOperationRequest request, CancellationToken token)
public async Task<DetectedOperationListDto> GetAsync(DetectedOperationByWellRequest request, CancellationToken token)
{
var dtos = await GetOperationsAsync(request, token);
if (dtos?.Any() != true)
return null;
return new DetectedOperationListDto();
var stats = GetOperationsDrillersStat(dtos);
var result = new DetectedOperationListDto
@ -45,23 +51,93 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return result;
}
public async Task<IEnumerable<DetectedOperationDto>> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token)
public async Task<IEnumerable<DetectedOperationWithDrillerDto>> GetOperationsAsync(DetectedOperationByWellRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null)
return Enumerable.Empty<DetectedOperationDto>();
return Enumerable.Empty<DetectedOperationWithDrillerDto>();
var query = BuildQuery(well, request).AsNoTracking();
var data = await query.ToListAsync(token);
var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
var data = await operationRepository.Get(requestByTelemetry, token);
var operationValues = await operationValueService.GetByIdWellAsync(request.IdWell, token);
var schedules = await scheduleService.GetByIdWellAsync(request.IdWell, token);
var dtos = data.Select(o => Convert(o, well, operationValues, schedules));
var dtos = data.Select(o => Convert(o, operationValues, schedules));
return dtos;
}
private static IEnumerable<DetectedOperationDrillersStatDto> GetOperationsDrillersStat(IEnumerable<DetectedOperationDto> operations)
public async Task<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(int? idWell, CancellationToken token)
{
if(idWell is null)
{
return wellOperationCategoryRepository.Get(false);
}
else
{
var well = await wellService.GetOrDefaultAsync((int )idWell, token);
if (well?.IdTelemetry is null)
return Enumerable.Empty<WellOperationCategoryDto>();
var request = new DetectedOperationByTelemetryRequest()
{
IdTelemetry = well.IdTelemetry.Value
};
var operations = await operationRepository.Get(request, token);
var categories = operations
.Select(o => o.OperationCategory)
.Distinct();
return categories;
}
}
public async Task<IEnumerable<DetectedOperationStatDto>> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null || well.Timezone is null)
return Enumerable.Empty<DetectedOperationStatDto>();
var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
var operations = await operationRepository.Get(requestByTelemetry, token);
if (!operations.Any())
return Enumerable.Empty<DetectedOperationStatDto>();
var operationValues = await operationValueService.GetByIdWellAsync(request.IdWell, token);
var dtos = operations
.GroupBy(o => (o.IdCategory, o.OperationCategory.Name))
.OrderBy(g => g.Key)
.Select(g => new DetectedOperationStatDto
{
IdCategory = g.Key.IdCategory,
Category = g.Key.Name,
Count = g.Count(),
MinutesAverage = g.Average(o => o.DurationMinutes),
MinutesMin = g.Min(o => o.DurationMinutes),
MinutesMax = g.Max(o => o.DurationMinutes),
MinutesTotal = g.Sum(o => o.DurationMinutes),
ValueAverage = g.Average(o => o.Value),
ValueMax = g.Max(o => o.Value),
ValueMin = g.Min(o => o.Value),
});
return dtos;
}
public async Task<int> DeleteAsync(DetectedOperationByWellRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null || well.Timezone is null)
return 0;
var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
var result = await operationRepository.Delete(-1, requestByTelemetry, token);
return result;
}
private static IEnumerable<DetectedOperationDrillersStatDto> GetOperationsDrillersStat(IEnumerable<DetectedOperationWithDrillerDto> operations)
{
var groups = operations.GroupBy(o => o.Driller);
@ -88,70 +164,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return stats;
}
public async Task<IEnumerable<DetectedOperationStatDto>?> GetOperationsStatAsync(DetectedOperationRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null || well.Timezone is null)
return null;
var query = BuildQuery(well, request)
?.AsNoTracking();
if (query is null)
return null;
var entities = await query
.Select(o => new {
o.IdCategory,
DurationMinutes = (o.DateEnd - o.DateStart).TotalMinutes,
o.Value,
})
.ToListAsync(token);
if (!entities.Any())
return null;
var operationValues = await operationValueService.GetByIdWellAsync(request.IdWell, token);
var categories = await query
.Select(o => new {o.IdCategory, o.OperationCategory.Name })
.Distinct()
.ToDictionaryAsync(c=>c.IdCategory, c=>c.Name, token);
var dtos = entities
.GroupBy(o => o.IdCategory)
.OrderBy(g => g.Key)
.Select(g => new DetectedOperationStatDto{
IdCategory = g.Key,
Category = categories[g.Key],
Count = g.Count(),
MinutesAverage = g.Average(o => o.DurationMinutes),
MinutesMin = g.Min(o => o.DurationMinutes),
MinutesMax = g.Max(o => o.DurationMinutes),
MinutesTotal = g.Sum(o => o.DurationMinutes),
ValueAverage = g.Average(o => o.Value),
ValueMax = g.Max(o => o.Value),
ValueMin = g.Min(o => o.Value),
});
return dtos;
}
public async Task<int> DeleteAsync(DetectedOperationRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well?.IdTelemetry is null || well.Timezone is null)
return 0;
var query = BuildQueryBase(well, request);
if (query is null)
return 0;
db.DetectedOperations.RemoveRange(query);
return await db.SaveChangesAsync(token);
}
private static bool IsTargetOk(DetectedOperationDto op)
private static bool IsTargetOk(DetectedOperationWithDrillerDto op)
{
return (op.IdCategory) switch
{
@ -162,7 +175,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
};
}
private static double DeltaToTarget(DetectedOperationDto op)
private static double DeltaToTarget(DetectedOperationWithDrillerDto op)
{
return (op.IdCategory) switch
{
@ -173,72 +186,15 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
};
}
private IQueryable<DetectedOperation> BuildQueryBase(WellDto well, DetectedOperationRequest request)
private static DetectedOperationWithDrillerDto Convert(DetectedOperationDto operation, IEnumerable<OperationValueDto> operationValues, IEnumerable<ScheduleDto> schedules)
{
var query = db.Set<DetectedOperation>()
.Where(o => o.IdTelemetry == well.IdTelemetry);
if (request.IdsTelemetries.Any())
{
query = query
.Union(db.Set<DetectedOperation>().Where(o => request.IdsTelemetries.Contains(o.IdTelemetry)));
}
if (request.IdsCategories.Any())
query = query.Where(o => request.IdsCategories.Contains(o.IdCategory));
if (request.GeDateStart is not null)
query = query.Where(o => o.DateStart >= request.GeDateStart.Value.Date.ToUtcDateTimeOffset(well.Timezone.Hours));
if (request.LeDateEnd is not null)
query = query.Where(o => o.DateEnd <= request.LeDateEnd.Value.Date.ToUtcDateTimeOffset(well.Timezone.Hours));
if (request.GeDepth is not null)
query = query.Where(o => o.DepthStart >= request.GeDepth);
if (request.LeDepth is not null)
query = query.Where(o => o.DepthEnd <= request.LeDepth);
if (request.IdTelemetryUser is not null)
query = query.Where(o => o.IdUsersAtStart == request.IdTelemetryUser);
return query;
}
private IQueryable<DetectedOperation> BuildQuery(WellDto well, DetectedOperationRequest request)
{
IQueryable<DetectedOperation> query = BuildQueryBase(well, request)
.Include(o => o.OperationCategory);
if (request.SortFields?.Any() == true)
{
query = query.SortBy(request.SortFields);
}
else
query = query
.OrderBy(o => o.DateStart)
.ThenBy(o => o.DepthStart);
if (request.Skip.HasValue)
query = query.Skip((int)request.Skip);
if (request.Take.HasValue)
query = query.Take((int)request.Take);
return query;
}
private static DetectedOperationDto Convert(DetectedOperation operation, WellDto well, IEnumerable<OperationValueDto> operationValues, IEnumerable<ScheduleDto> schedules)
{
var dto = operation.Adapt<DetectedOperationDto>();
dto.IdWell = well.Id;
var dateStart = operation.DateStart.ToRemoteDateTime(well.Timezone.Hours);
dto.DateStart = dateStart;
dto.DateEnd = operation.DateEnd.ToRemoteDateTime(well.Timezone.Hours);
var dto = operation.Adapt<DetectedOperationWithDrillerDto>();
dto.OperationValue = operationValues.FirstOrDefault(v => v.IdOperationCategory == dto.IdCategory
&& v.DepthStart <= dto.DepthStart
&& v.DepthEnd > dto.DepthStart);
var dateStart = dto.DateStart;
var timeStart = new TimeDto(dateStart);
var driller = schedules.FirstOrDefault(s =>
s.DrillStart <= dateStart &&
@ -249,36 +205,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
))
?.Driller;
dto.Driller = driller;
return dto;
}
public async Task<IEnumerable<WellOperationCategoryDto>?> GetCategoriesAsync(int? idWell, CancellationToken token)
{
IQueryable<WellOperationCategory> query;
if(idWell is null)
{
query = db.WellOperationCategories;
}
else
{
var well = await wellService.GetOrDefaultAsync((int )idWell, token);
if (well?.IdTelemetry is null)
return null;
var idTelemetry = (int)well.IdTelemetry;
query = db.DetectedOperations
.Include(o => o.OperationCategory)
.Where(o => o.IdTelemetry == idTelemetry)
.Select(o => o.OperationCategory)
.Distinct();
}
var result = await query
.Where(c => c.Id < 1000)
.AsNoTracking()
.Select(c => c.Adapt<WellOperationCategoryDto>())
.ToArrayAsync(token);
return result;
}
}
}

View File

@ -45,17 +45,16 @@ internal class SubsystemService : ISubsystemService
if(!well.IdTelemetry.HasValue)
return Enumerable.Empty<SubsystemStatDto>();
var detectedOperationSummaryRequest = new DetectedOperationRequest
var detectedOperationSummaryRequest = new DetectedOperationByWellRequest
{
IdWell = request.IdWell,
IdsTelemetries = new[] { well.IdTelemetry.Value },
IdsCategories = WellOperationCategory.MechanicalDrillingSubIds,
GeDateStart = request.GeDate,
LeDateEnd = request.LeDate,
GeDepth = request.GeDepth,
LeDepth = request.LeDepth,
GeDepthStart = request.GeDepth,
LeDepthEnd = request.LeDepth,
};
var operations = await detectedOperationService.GetOperationsAsync(detectedOperationSummaryRequest,
@ -78,7 +77,7 @@ internal class SubsystemService : ISubsystemService
return result;
}
private async Task<IEnumerable<SubsystemStatDto>> CalcStatAsync(IEnumerable<DetectedOperationDto> operations, CancellationToken token)
private async Task<IEnumerable<SubsystemStatDto>> CalcStatAsync(IEnumerable<DetectedOperationWithDrillerDto> operations, CancellationToken token)
{
if (!subsystems.Any())
subsystems = (await subsystemRepository.GetAllAsync(token)).ToDictionary(s => s.Id, s => s);
@ -92,7 +91,7 @@ internal class SubsystemService : ISubsystemService
return stat;
}
private SubsystemStatDto CalcOscillationStat(IEnumerable<DetectedOperationDto> operations)
private SubsystemStatDto CalcOscillationStat(IEnumerable<DetectedOperationWithDrillerDto> operations)
{
operations = operations.Where(o => o.IdCategory == WellOperationCategory.IdSlide);
@ -114,7 +113,7 @@ internal class SubsystemService : ISubsystemService
return oscillationStat;
}
private IEnumerable<SubsystemStatDto> CalcApdStat(IEnumerable<DetectedOperationDto> operations)
private IEnumerable<SubsystemStatDto> CalcApdStat(IEnumerable<DetectedOperationWithDrillerDto> operations)
{
var apdRotorAndSlide = operations
.Where(o => WellOperationCategory.MechanicalDrillingSubIds.Contains(o.IdCategory))
@ -169,7 +168,7 @@ internal class SubsystemService : ISubsystemService
}
private static (double SumDepthInterval, double UsedTimeHours, int Count) AggregateOperations(int idSubsystem,
IEnumerable<DetectedOperationDto> operations) =>
IEnumerable<DetectedOperationWithDrillerDto> operations) =>
idSubsystem switch
{
IdSubsystemAPDRotor => CalcOperationsByEnableSubsystems(operations, EnabledSubsystemsFlags.AutoRotor),
@ -180,7 +179,7 @@ internal class SubsystemService : ISubsystemService
};
private static (double SumDepthInterval, double UsedTimeHours, int OperationCount) CalcOperationsByEnableSubsystems(
IEnumerable<DetectedOperationDto> operations,
IEnumerable<DetectedOperationWithDrillerDto> operations,
EnabledSubsystemsFlags enabledSubsystems)
{
var filtered = operations.Where(o => enabledSubsystems.HasEnabledSubsystems(o.EnabledSubsystems));
@ -215,7 +214,7 @@ internal class SubsystemService : ISubsystemService
var leDateUtc = leDate?.ToUtcDateTimeOffset(hoursOffset);
var request = new DetectedOperationRequest
var request = new DetectedOperationByWellRequest
{
IdWell = well.Id,
IdsCategories = WellOperationCategory.MechanicalDrillingSubIds,

View File

@ -36,13 +36,18 @@ namespace AsbCloudInfrastructure.Services
.ThenInclude(r => r.Company)
.AsNoTracking();
public WellService(IAsbCloudDbContext db, IMemoryCache memoryCache, ITelemetryService telemetryService, ITimezoneService timezoneService, WellInfoService wellInfoService)
public WellService(IAsbCloudDbContext db,
IMemoryCache memoryCache,
ITelemetryService telemetryService,
ITimezoneService timezoneService,
WellInfoService wellInfoService,
IWellOperationCategoryRepository wellOperationCategoryRepository)
: base(db, memoryCache, MakeQueryWell)
{
this.telemetryService = telemetryService;
this.timezoneService = timezoneService;
this.wellInfoService = wellInfoService;
this.wellOperationRepository = new WellOperationRepository(db, memoryCache, this);
this.wellOperationRepository = new WellOperationRepository(db, memoryCache, this, wellOperationCategoryRepository);
companyTypesService = new CrudCacheRepositoryBase<CompanyTypeDto, CompanyType>(dbContext, memoryCache);
}

View File

@ -10,6 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.26" />
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="8.0.1">
<PrivateAssets>all</PrivateAssets>

View File

@ -57,7 +57,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[HttpGet]
[ProducesResponseType(typeof(DetectedOperationListDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetAsync(
[FromQuery] DetectedOperationRequest request,
[FromQuery] DetectedOperationByWellRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
@ -76,7 +76,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[HttpGet("stat")]
[ProducesResponseType(typeof(IEnumerable<DetectedOperationStatDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetStatAsync(
[FromQuery] DetectedOperationRequest request,
[FromQuery] DetectedOperationByWellRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
@ -98,7 +98,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[Permission]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> DeleteAsync(
[FromQuery] DetectedOperationRequest request,
[FromQuery] DetectedOperationByWellRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))