diff --git a/AsbCloudApp/Data/WellOperationDataDto.cs b/AsbCloudApp/Data/WellOperationDataDto.cs
new file mode 100644
index 00000000..874bd65c
--- /dev/null
+++ b/AsbCloudApp/Data/WellOperationDataDto.cs
@@ -0,0 +1,39 @@
+namespace AsbCloudApp.Data
+{
+ /// Операция на скважине
+ public class WellOperationDataDto : IWellRelated
+ {
+ ///
+ public int IdWell { get; set; }
+
+ ///
+ /// id секции скважины
+ ///
+ public int IdWellSectionType { get; set; }
+
+ ///
+ /// id категории операции
+ ///
+ public int IdCategory { get; set; }
+
+ ///
+ /// Глубина на начало операции, м
+ ///
+ public double DepthStart { get; set; }
+
+ ///
+ /// Продолжительность, часы
+ ///
+ public double DurationHours { get; set; }
+
+ ///
+ /// Наименование секции
+ ///
+ public string WellSectionTypeCaption { get; set; } = string.Empty;
+
+ ///
+ /// Наименование категории
+ ///
+ public string OperationCategoryName { get; set; } = string.Empty;
+ }
+}
diff --git a/AsbCloudApp/Repositories/IWellOperationRepository.cs b/AsbCloudApp/Repositories/IWellOperationRepository.cs
index cf277304..dd39ce52 100644
--- a/AsbCloudApp/Repositories/IWellOperationRepository.cs
+++ b/AsbCloudApp/Repositories/IWellOperationRepository.cs
@@ -42,6 +42,14 @@ namespace AsbCloudApp.Repositories
///
Task> GetAsync(WellOperationRequest request, CancellationToken token);
+ ///
+ /// Получить список операций по запросу
+ ///
+ ///
+ ///
+ ///
+ Task> GetAsync(WellsOperationRequest request, CancellationToken token);
+
///
/// Получить страницу списка операций
///
diff --git a/AsbCloudApp/Requests/ParserOptions/WellRelatedParserRequest.cs b/AsbCloudApp/Requests/ParserOptions/WellRelatedParserRequest.cs
new file mode 100644
index 00000000..3ffbf4d5
--- /dev/null
+++ b/AsbCloudApp/Requests/ParserOptions/WellRelatedParserRequest.cs
@@ -0,0 +1,21 @@
+namespace AsbCloudApp.Requests.ParserOptions;
+
+///
+/// Параметры парсинга
+///
+public class WellRelatedParserRequest : IParserOptionsRequest
+{
+ ///
+ /// Конструктор
+ ///
+ /// Id скважины
+ public WellRelatedParserRequest(int idWell)
+ {
+ IdWell = idWell;
+ }
+
+ ///
+ /// Id скважины
+ ///
+ public int IdWell { get; }
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Requests/WellOperationRequest.cs b/AsbCloudApp/Requests/WellOperationRequest.cs
index ab5b901e..1d190018 100644
--- a/AsbCloudApp/Requests/WellOperationRequest.cs
+++ b/AsbCloudApp/Requests/WellOperationRequest.cs
@@ -6,7 +6,7 @@ namespace AsbCloudApp.Requests
///
/// параметры для запроса списка операций
///
- public class WellOperationRequestBase: RequestBase
+ public class WellOperationRequestBase : RequestBase
{
///
/// фильтр по дате начала операции
@@ -42,12 +42,40 @@ namespace AsbCloudApp.Requests
/// фильтр по списку id конструкций секции
///
public IEnumerable? SectionTypeIds { 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
+ public class WellOperationRequest : WellOperationRequestBase
{
///
/// id скважины
@@ -57,7 +85,7 @@ namespace AsbCloudApp.Requests
///
/// ctor
///
- public WellOperationRequest(){}
+ public WellOperationRequest() { }
///
/// копирующий конструктор
@@ -65,21 +93,20 @@ namespace AsbCloudApp.Requests
///
///
public WellOperationRequest(WellOperationRequestBase request, int idWell)
+ :base(request)
{
- this.IdWell = idWell;
-
- this.GeDepth = request.GeDepth;
- this.LeDepth = request.LeDepth;
- this.GeDate = request.GeDate;
- this.LtDate = request.LtDate;
-
- this.OperationCategoryIds = request.OperationCategoryIds;
- this.OperationType = request.OperationType;
- this.SectionTypeIds = request.SectionTypeIds;
-
- this.Skip= request.Skip;
- this.Take= request.Take;
- this.SortFields = request.SortFields;
+ IdWell = idWell;
}
}
+
+ ///
+ /// Параметры для запроса списка операций (с массивом id скважин)
+ ///
+ public class WellsOperationRequest : WellOperationRequestBase
+ {
+ ///
+ /// ids скважин
+ ///
+ public IEnumerable IdsWell { get; set; } = null!;
+ }
}
diff --git a/AsbCloudApp/Services/IParserService.cs b/AsbCloudApp/Services/IParserService.cs
index 8c01af5a..6fa095cd 100644
--- a/AsbCloudApp/Services/IParserService.cs
+++ b/AsbCloudApp/Services/IParserService.cs
@@ -8,8 +8,10 @@ namespace AsbCloudApp.Services;
/// Сервис парсинга
///
///
-public interface IParserService : IParserService
+///
+public interface IParserService : IParserService
where TDto : class, IId
+ where TOptions : IParserOptionsRequest
{
///
/// Распарсить файл
@@ -17,8 +19,7 @@ public interface IParserService : IParserService
///
///
///
- ParserResultDto Parse(Stream file, TOptions options)
- where TOptions : IParserOptionsRequest;
+ ParserResultDto Parse(Stream file, TOptions options);
///
/// Получение шаблона для заполнения
diff --git a/AsbCloudApp/Services/IWellCompositeOperationService.cs b/AsbCloudApp/Services/IWellCompositeOperationService.cs
new file mode 100644
index 00000000..94ba1f93
--- /dev/null
+++ b/AsbCloudApp/Services/IWellCompositeOperationService.cs
@@ -0,0 +1,21 @@
+using AsbCloudApp.Data;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudApp.Services
+{
+ ///
+ /// Интерфейс для вычисления композитной скважины
+ ///
+ public interface IWellCompositeOperationService
+ {
+ ///
+ /// Получение данных для построения композитной скважины
+ ///
+ ///
+ ///
+ ///
+ Task>> GetAsync(IEnumerable idsWells, CancellationToken token);
+ }
+}
diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs
index 2111356b..6250112f 100644
--- a/AsbCloudInfrastructure/DependencyInjection.cs
+++ b/AsbCloudInfrastructure/DependencyInjection.cs
@@ -198,6 +198,7 @@ namespace AsbCloudInfrastructure
services.AddTransient, CrudCacheRepositoryBase>();
services.AddTransient();
+ services.AddTransient();
// admin crud services:
services.AddTransient, CrudCacheRepositoryBase>(s =>
diff --git a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
index 57bf3768..329d2502 100644
--- a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
+++ b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
@@ -205,6 +205,17 @@ public class WellOperationRepository : IWellOperationRepository
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,
@@ -385,7 +396,6 @@ public class WellOperationRepository : IWellOperationRepository
.Include(s => s.OperationCategory)
.Where(o => o.IdWell == request.IdWell);
-
if (request.OperationType.HasValue)
query = query.Where(e => e.IdType == request.OperationType.Value);
@@ -420,6 +430,7 @@ public class WellOperationRepository : IWellOperationRepository
.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,
@@ -472,6 +483,50 @@ public class WellOperationRepository : IWellOperationRepository
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);
diff --git a/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs b/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs
index 661c729f..13c621af 100644
--- a/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs
+++ b/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs
@@ -12,8 +12,9 @@ using Mapster;
namespace AsbCloudInfrastructure.Services.Parser;
-public abstract class ParserExcelService : IParserService
+public abstract class ParserExcelService : IParserService
where TDto : class, IValidatableObject, IId
+ where TOptions : IParserOptionsRequest
{
protected abstract string SheetName { get; }
@@ -22,9 +23,8 @@ public abstract class ParserExcelService : IParserService
protected abstract string TemplateFileName { get; }
protected abstract IDictionary Cells { get; }
-
- public virtual ParserResultDto Parse(Stream file, TOptions options)
- where TOptions : IParserOptionsRequest
+
+ public virtual ParserResultDto Parse(Stream file, TOptions options)
{
using var workbook = new XLWorkbook(file);
var sheet = workbook.GetWorksheet(SheetName);
diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs
index c1717db1..f4e829fc 100644
--- a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs
+++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs
@@ -6,7 +6,6 @@ using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Repositories;
using AsbCloudInfrastructure.Services.Parser;
-using Microsoft.Extensions.DependencyInjection;
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs
index 1686779b..effe7ef6 100644
--- a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs
+++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs
@@ -1,14 +1,26 @@
-using System;
+using System.IO;
+using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps;
+using AsbCloudApp.Requests.ParserOptions;
using AsbCloudInfrastructure.Services.Parser;
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
-public abstract class ProcessMapPlanParser : ParserExcelService
+public abstract class ProcessMapPlanParser : ParserExcelService
where TDto : ProcessMapPlanBaseDto
{
protected override int HeaderRowsCount => 2;
+ public override ParserResultDto Parse(Stream file, WellRelatedParserRequest options)
+ {
+ var result = base.Parse(file, options);
+
+ foreach (var item in result.Item)
+ item.Item.IdWell = options.IdWell;
+
+ return result;
+ }
+
protected static int? GetIdMode(string? modeName) =>
modeName?.Trim().ToLower() switch
{
diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanReamParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanReamParser.cs
index 20aca5ed..6ca31714 100644
--- a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanReamParser.cs
+++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanReamParser.cs
@@ -22,14 +22,12 @@ public class ProcessMapPlanReamParser : ProcessMapPlanParser "ProcessMapPlanReamTemplate.xlsx";
private const int ColumnSection = 1;
- private const int ColumnMode = 2;
-
+
protected override IDictionary Cells => new Dictionary
{
{ nameof(ProcessMapPlanReamDto.Section), new Cell(ColumnSection, typeof(string)) },
{ nameof(ProcessMapPlanReamDto.DepthStart), new Cell(2, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.DepthEnd), new Cell(3, typeof(double)) },
-
{ nameof(ProcessMapPlanReamDto.Repeats), new Cell(4, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.SpinUpward), new Cell(5, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.SpinUpward), new Cell(6, typeof(double)) },
diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs
index 5de4e2c5..cf4b7118 100644
--- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs
+++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs
@@ -4,12 +4,10 @@ using AsbCloudInfrastructure.Services.Parser;
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
-public class TrajectoryFactManualParser : ParserExcelService
+public class TrajectoryFactManualParser : TrajectoryParser
{
protected override string SheetName => "Фактическая траектория";
-
- protected override int HeaderRowsCount => 2;
-
+
protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx";
protected override IDictionary Cells => new Dictionary
diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs
new file mode 100644
index 00000000..ce579fa6
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs
@@ -0,0 +1,23 @@
+using System.IO;
+using AsbCloudApp.Data;
+using AsbCloudApp.Data.Trajectory;
+using AsbCloudApp.Requests.ParserOptions;
+using AsbCloudInfrastructure.Services.Parser;
+
+namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
+
+public abstract class TrajectoryParser : ParserExcelService
+ where TDto : TrajectoryGeoDto
+{
+ protected override int HeaderRowsCount => 2;
+
+ public override ParserResultDto Parse(Stream file, WellRelatedParserRequest options)
+ {
+ var result = base.Parse(file, options);
+
+ foreach (var item in result.Item)
+ item.Item.IdWell = options.IdWell;
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs
index 4c100766..568bfa35 100644
--- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs
+++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs
@@ -4,12 +4,10 @@ using AsbCloudInfrastructure.Services.Parser;
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
-public class TrajectoryPlanParser : ParserExcelService
+public class TrajectoryPlanParser : TrajectoryParser
{
protected override string SheetName => "Плановая траектория";
-
- protected override int HeaderRowsCount => 2;
-
+
protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx";
protected override IDictionary Cells => new Dictionary
diff --git a/AsbCloudInfrastructure/Services/WellCompositeOperationService.cs b/AsbCloudInfrastructure/Services/WellCompositeOperationService.cs
new file mode 100644
index 00000000..6ba97ea0
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/WellCompositeOperationService.cs
@@ -0,0 +1,220 @@
+using AsbCloudApp.Data;
+using AsbCloudApp.Repositories;
+using AsbCloudApp.Requests;
+using AsbCloudApp.Services;
+using AsbCloudDb.Model;
+using Microsoft.EntityFrameworkCore;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudInfrastructure.Services
+{
+ public class WellCompositeOperationService : IWellCompositeOperationService
+ {
+ private ICrudRepository wellSectionTypeRepository;
+ private IWellOperationCategoryRepository wellOperationCategoryRepository;
+ private IWellOperationRepository wellOperationRepository;
+
+ ///
+ /// Тип секции "Транспортный стол"
+ ///
+ private const int wellSectionTransportTable = 5;
+
+ ///
+ /// Тип секции "Эксплуатационная колонна"
+ ///
+ private const int wellSectionProductionString = 4;
+
+
+ ///
+ /// набор настроек для замены одной категории секции на другую
+ ///
+ private static Dictionary<(int, int), int> SettingsForSectionCategoryChange = new Dictionary<(int, int), int>() {
+ { (2, 5096), 5013 },
+ { (2, 5008), 5013 },
+ { (3, 5096), 5084 },
+ { (3, 5008), 5084 },
+ { (3, 5085), 5015 },
+ { (3, 5014), 5015 },
+ { (31, 5014), 5015 },
+ { (31, 5012), 5013 },
+ { (31, 5083), 5013 },
+ { (4, 5085), 5015 },
+ { (4, 5087), 5015 },
+ { (4, 5014), 5015 },
+ { (4, 5053), 5037 },
+ { (4, 5084), 5096 },
+ { (4, 5086), 5013 },
+ { (6, 5085), 5015 },
+ { (6, 5036), 5034 },
+ { (6, 5035), 5097 }
+ };
+
+ private HashSet<(int IdSectionType, int IdCategory)> WellSectionTypesWithCategories = new HashSet<(int IdSectionType, int IdCategory)>()
+ {
+ { (2, 5001) },
+ { (2, 5003) },
+ { (2, 5013) },
+ { (2, 5000) },
+ { (2, 5022) },
+ { (2, 5017) },
+ { (2, 5023) },
+ { (2, 4007) },
+ { (2, 5090) },
+ { (3, 5001) },
+ { (3, 5015) },
+ { (3, 5037) },
+ { (3, 5057) },
+ { (3, 5003) },
+ { (3, 5036) },
+ { (3, 5084) },
+ { (3, 5013) },
+ { (3, 5000) },
+ { (3, 5022) },
+ { (3, 5017) },
+ { (3, 4007) },
+ { (3, 5090) },
+ { (3, 5045) },
+ { (3, 5042) },
+ { (3, 5046) },
+ { (31, 5001) },
+ { (31, 5015) },
+ { (31, 5037) },
+ { (31, 5057) },
+ { (31, 5003) },
+ { (31, 5036) },
+ { (31, 5013) },
+ { (31, 5022) },
+ { (31, 5017) },
+ { (31, 5023) },
+ { (31, 4007) },
+ { (31, 5045) },
+ { (31, 5042) },
+ { (31, 5046) },
+ { (4, 5001) },
+ { (4, 5015) },
+ { (4, 5046) },
+ { (4, 5037) },
+ { (4, 5097) },
+ { (4, 5057) },
+ { (4, 5003) },
+ { (4, 5036) },
+ { (4, 5008) },
+ { (4, 5003) },
+ { (4, 5036) },
+ { (4, 5013) },
+ { (4, 5000) },
+ { (4, 5029) },
+ { (4, 5022) },
+ { (4, 5017) },
+ { (4, 5019) },
+ { (4, 5042) },
+ { (4, 5046) },
+ { (6, 5001) },
+ { (6, 5015) },
+ { (6, 5034) },
+ { (6, 5037) },
+ { (6, 5097) },
+ { (6, 5057) },
+ { (6, 5003) }
+ };
+
+
+
+ public WellCompositeOperationService(
+ ICrudRepository wellSectionTypeRepository,
+ IWellOperationCategoryRepository wellOperationCategoryRepository,
+ IWellOperationRepository wellOperationRepository)
+ {
+ this.wellSectionTypeRepository = wellSectionTypeRepository;
+ this.wellOperationCategoryRepository = wellOperationCategoryRepository;
+ this.wellOperationRepository = wellOperationRepository;
+ }
+
+ public async Task>> GetAsync(IEnumerable idsWells, CancellationToken token)
+ {
+ var sections = await wellSectionTypeRepository.GetAllAsync(token);
+ var sectionsDict = sections.ToDictionary(s => s.Id, s => s.Caption);
+
+ var categories = wellOperationCategoryRepository.Get(true);
+ var categoriesDict = categories.ToDictionary(s => s.Id, s => s.Name);
+
+ var idsWellSectionTypes = WellSectionTypesWithCategories.Select(t => t.IdSectionType).Distinct();
+ var usedCategories = WellSectionTypesWithCategories.Select(c => c.IdCategory).Distinct();
+
+ var wellOperationRequest = new WellsOperationRequest()
+ {
+ IdsWell = idsWells,
+ OperationCategoryIds = usedCategories,
+ SectionTypeIds = idsWellSectionTypes,
+ OperationType = WellOperation.IdOperationTypeFact
+ };
+ var operations = await wellOperationRepository.GetAsync(wellOperationRequest, token);
+
+ var renamedOperations = operations.Select(o => UpdateIdWellSectionAndIdCategory(o, sectionsDict, categoriesDict));
+
+ var wellOperationsWithComposite = new List>();
+ var compositeDepth = 0d;
+ foreach ((int IdSection, int IdCategory) in WellSectionTypesWithCategories)
+ {
+ var filteredByTemplate = renamedOperations
+ .Where(o => o.IdWellSectionType == IdSection)
+ .Where(o => o.IdCategory == IdCategory);
+
+ if (!filteredByTemplate.Any())
+ continue;
+
+ var groupedByWell = filteredByTemplate.GroupBy(o => o.IdWell);
+
+ var aggreagtedByWell = groupedByWell.Select(g => new WellOperationDataDto
+ {
+ IdCategory = IdCategory,
+ IdWell = g.Key,
+ IdWellSectionType = IdSection,
+ DepthStart = g.Min(o => o.DepthStart),
+ DurationHours = g.Sum(o => o.DurationHours),
+ OperationCategoryName = g.First().OperationCategoryName,
+ WellSectionTypeCaption = g.First().WellSectionTypeCaption,
+ });
+
+ var composite = aggreagtedByWell.OrderBy(o => o.DurationHours).
+ ThenByDescending(o => o.DepthStart)
+ .First();
+
+ composite.IdWell = 0;
+ if (compositeDepth > composite.DepthStart)
+ composite.DepthStart = compositeDepth;
+
+ compositeDepth = composite.DepthStart;
+
+ var resultItem = aggreagtedByWell.ToDictionary(o => o.IdWell);
+ resultItem.Add(0, composite);
+
+ wellOperationsWithComposite.Add(resultItem);
+ }
+ return wellOperationsWithComposite;
+ }
+
+ private static WellOperationDataDto UpdateIdWellSectionAndIdCategory(
+ WellOperationDataDto dto,
+ Dictionary sectionTypes,
+ Dictionary operationCategories)
+ {
+ if (dto.IdWellSectionType == wellSectionTransportTable)
+ {
+ dto.IdWellSectionType = wellSectionProductionString;
+ dto.WellSectionTypeCaption = sectionTypes[dto.IdWellSectionType] ?? string.Empty;
+ }
+
+ if ((SettingsForSectionCategoryChange.TryGetValue((dto.IdWellSectionType, dto.IdCategory), out int newIdCategory)))
+ {
+ dto.IdCategory = newIdCategory;
+ dto.OperationCategoryName = operationCategories[dto.IdCategory] ?? string.Empty;
+ }
+
+ return dto;
+ }
+ }
+}
diff --git a/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs b/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs
index 2a151573..93ce768b 100644
--- a/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs
+++ b/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs
@@ -35,5 +35,5 @@ public interface IProcessMapPlanDrillingClient
[Multipart]
[Post(BaseRoute + "/parse")]
- Task>> Parse(int idWell, [AliasAs("files")] IEnumerable streams);
+ Task>> Parse(int idWell, [AliasAs("file")] StreamPart stream);
}
diff --git a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs
index 8921d982..8b9bd3e9 100644
--- a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs
+++ b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs
@@ -11,9 +11,11 @@ public class TrajectoryParserTest
{
private const string UsingTemplateFile = "AsbCloudWebApi.Tests.Services.Trajectory.Templates";
+ private readonly WellRelatedParserRequest options = new(1);
+
private readonly TrajectoryPlanParser trajectoryPlanParser = new();
private readonly TrajectoryFactManualParser trajectoryFactManualParser = new();
-
+
[Fact]
public void Parse_trajectory_plan()
{
@@ -22,8 +24,8 @@ public class TrajectoryParserTest
if (stream is null)
Assert.Fail("Файла для импорта не существует");
-
- var trajectoryRows = trajectoryPlanParser.Parse(stream, IParserOptionsRequest.Empty());
+
+ var trajectoryRows = trajectoryPlanParser.Parse(stream, options);
Assert.Equal(3, trajectoryRows.Item.Count());
}
@@ -37,7 +39,7 @@ public class TrajectoryParserTest
if (stream is null)
Assert.Fail("Файла для импорта не существует");
- var trajectoryRows = trajectoryFactManualParser.Parse(stream, IParserOptionsRequest.Empty());
+ var trajectoryRows = trajectoryFactManualParser.Parse(stream, options);
Assert.Equal(4, trajectoryRows.Item.Count());
}
diff --git a/AsbCloudWebApi.Tests/Services/WellCompositeOperation/WellCompositeOperationServiceTest.cs b/AsbCloudWebApi.Tests/Services/WellCompositeOperation/WellCompositeOperationServiceTest.cs
new file mode 100644
index 00000000..d4659d8a
--- /dev/null
+++ b/AsbCloudWebApi.Tests/Services/WellCompositeOperation/WellCompositeOperationServiceTest.cs
@@ -0,0 +1,337 @@
+using AsbCloudApp.Requests;
+using AsbCloudDb.Model;
+using AsbCloudInfrastructure.Repository;
+using AsbCloudInfrastructure.Services.ProcessMaps.Report;
+using AsbCloudInfrastructure.Services;
+using NSubstitute;
+using ProtoBuf.Meta;
+using SignalRSwaggerGen.Enums;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+using AsbCloudApp.Repositories;
+using AsbCloudApp.Data;
+using AsbCloudApp.Services;
+
+namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
+{
+ public class WellCompositeOperationServiceTest
+ {
+ private WellCompositeOperationService service;
+
+ private ICrudRepository wellSectionTypeRepository
+ = Substitute.For>();
+ private IWellOperationCategoryRepository wellOperationCategoryRepository
+ = Substitute.For();
+ private IWellOperationRepository wellOperationRepository
+ = Substitute.For();
+
+
+
+ private readonly static IEnumerable operationCategories = new List()
+ {
+ new(){Id = 5096, Name = "Шаблонирование перед спуском"},
+ new(){Id = 5008, Name = "Шаблонировка во время бурения"},
+ new(){Id = 5013, Name = "Подъем КНБК"},
+ new(){Id = 5003, Name = "Бурение ротором"},
+ new(){Id = 5036, Name = "Промывка"},
+ new(){Id = 5012, Name = "Подъем инструмента"},
+ new(){Id = 5083, Name = "Проработка принудительная"},
+ };
+
+ private readonly static IEnumerable sectionTypes = new List()
+ {
+ new() {Id = 2, Caption = "Направление", Order = 0},
+ new() {Id = 3, Caption = "Кондуктор", Order = 1},
+ new() {Id = 31, Caption = "Техническая колонна", Order = 2}
+ };
+
+ private readonly static IEnumerable wellOperations1 = new List()
+ {
+ new WellOperationDataDto()
+ {
+ DepthStart = 50,
+ DurationHours = 1,
+ IdCategory = 5096,
+ IdWell = 55,
+ IdWellSectionType = 2,
+ OperationCategoryName = "Шаблонирование перед спуском",
+ WellSectionTypeCaption = "Направление"
+ },
+ new WellOperationDataDto()
+ {
+ DepthStart = 84,
+ DurationHours = 1,
+ IdCategory = 5008,
+ IdWell = 64,
+ IdWellSectionType = 2,
+ OperationCategoryName = "Шаблонировка во время бурения",
+ WellSectionTypeCaption = "Направление"
+ }
+ };
+
+ private readonly static IEnumerable wellOperations2 = new List()
+ {
+ new WellOperationDataDto()
+ {
+ DepthStart = 10,
+ DurationHours = 1.5,
+ IdCategory = 5003,
+ IdWell = 55,
+ IdWellSectionType = 2,
+ OperationCategoryName = "Бурение ротором",
+ WellSectionTypeCaption = "Направление"
+ },
+ new WellOperationDataDto()
+ {
+ DepthStart = 20,
+ DurationHours = 3.5,
+ IdCategory = 5003,
+ IdWell = 64,
+ IdWellSectionType = 2,
+ OperationCategoryName = "Бурение ротором",
+ WellSectionTypeCaption = "Направление"
+ }
+ };
+
+ private readonly static IEnumerable wellOperations3 = new List()
+ {
+ new WellOperationDataDto()
+ {
+ DepthStart = 1372,
+ DurationHours = 3,
+ IdCategory = 5036,
+ IdWell = 55,
+ IdWellSectionType = 3,
+ OperationCategoryName = "Промывка",
+ WellSectionTypeCaption = "Кондуктор"
+ },
+ new WellOperationDataDto()
+ {
+ DepthStart = 1435,
+ DurationHours = 4,
+ IdCategory = 5036,
+ IdWell = 64,
+ IdWellSectionType = 3,
+ OperationCategoryName = "Промывка",
+ WellSectionTypeCaption = "Кондуктор"
+ }
+ };
+
+ private readonly static IEnumerable wellOperations4 = new List()
+ {
+ new WellOperationDataDto()
+ {
+ DepthStart = 1000,
+ DurationHours = 10,
+ IdCategory = 5012,
+ IdWell = 55,
+ IdWellSectionType = 31,
+ OperationCategoryName = "Подъем инструмента",
+ WellSectionTypeCaption = "Техническая колонна"
+ },
+ new WellOperationDataDto()
+ {
+ DepthStart = 500,
+ DurationHours = 5,
+ IdCategory = 5083,
+ IdWell = 55,
+ IdWellSectionType = 31,
+ OperationCategoryName = "Проработка принудительная",
+ WellSectionTypeCaption = "Техническая колонна"
+ },
+ new WellOperationDataDto()
+ {
+ DepthStart = 600,
+ DurationHours = 5,
+ IdCategory = 5083,
+ IdWell = 64,
+ IdWellSectionType = 31,
+ OperationCategoryName = "Проработка принудительная",
+ WellSectionTypeCaption = "Техническая колонна"
+ }
+ };
+
+ public WellCompositeOperationServiceTest()
+ {
+
+ wellSectionTypeRepository.GetAllAsync(Arg.Any())
+ .Returns(sectionTypes);
+
+ wellOperationCategoryRepository.Get(Arg.Any())
+ .Returns(operationCategories);
+
+ service = new WellCompositeOperationService(
+ wellSectionTypeRepository,
+ wellOperationCategoryRepository,
+ wellOperationRepository);
+ }
+
+ ///
+ /// На вход подаются 2 операции с одинаковыми секциями (id = 2), но разными категориями (ids = 5096, 5008) и ключами скважин (ids = 55, 64)
+ /// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная
+ /// Операция должна иметь категорию 5013 для всех трех скважин
+ ///
+ ///
+ [Fact]
+ public async Task GetAsync_return_composite_and_others_with_category_5013()
+ {
+ // arrange
+ wellOperationRepository.GetAsync(Arg.Any(), Arg.Any())
+ .Returns(wellOperations1);
+
+ var idsWell = new List() { 55, 64 };
+
+ // act
+ var result = await service.GetAsync(idsWell, CancellationToken.None);
+
+ // assert
+ var compositeWellOperation = result.SelectMany(o => o.Values.Where(o => o.IdWell == 0)).FirstOrDefault();
+ Assert.NotNull(compositeWellOperation);
+ Assert.Equal(5013, compositeWellOperation.IdCategory);
+ Assert.Equal(1, compositeWellOperation.DurationHours);
+ Assert.Equal(84, compositeWellOperation.DepthStart);
+
+ var currentWellOperations = result.SelectMany(o => o.Values.Where(o => o.IdWell != 0));
+ var categories = currentWellOperations.Select(o => o.IdCategory).Distinct();
+ Assert.NotNull(categories);
+ Assert.Single(categories);
+ Assert.Equal(5013, categories.First());
+
+ var categoryName = currentWellOperations.Select(o => o.OperationCategoryName).First();
+ Assert.Equal("Подъем КНБК", categoryName);
+ }
+
+ ///
+ /// На вход подаются 2 операции с одинаковыми секциями (id = 2) и категориями (id = 5003), но разными ключами скважин (ids = 55, 64)
+ /// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная
+ /// Операция композитной скважины должна содержать данные той операции, которая содержит минимальный duration_hours
+ ///
+ ///
+ [Fact]
+ public async Task GetAsync_return_composite_with_minimum_depth_start()
+ {
+ // arrange
+ wellOperationRepository.GetAsync(Arg.Any(), Arg.Any())
+ .Returns(wellOperations2);
+
+ var idsWell = new List() { 55, 64 };
+
+ // act
+ var result = await service.GetAsync(idsWell, CancellationToken.None);
+
+ // assert
+ var compositeWellOperation = result.SelectMany(o => o.Values.Where(o => o.IdWell == 0)).FirstOrDefault();
+ Assert.NotNull(compositeWellOperation);
+ Assert.Equal(5003, compositeWellOperation.IdCategory);
+ Assert.Equal(1.5, compositeWellOperation.DurationHours);
+ Assert.Equal(10, compositeWellOperation.DepthStart);
+ }
+
+ ///
+ /// На вход подаются 2 операции с одинаковыми секциями (id = 3) и категориями (id = 5036), но разными ключами скважин (ids = 55, 64)
+ /// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная
+ /// Операция композитной скважины должна содержать данные той операции, которая содержит минимальный duration_hours
+ ///
+ ///
+ [Fact]
+ public async Task GetAsync_return_data3()
+ {
+ // arrange
+ wellOperationRepository.GetAsync(Arg.Any(), Arg.Any())
+ .Returns(wellOperations3);
+
+ var idsWell = new List() { 55, 64 };
+
+ // act
+ var result = await service.GetAsync(idsWell, CancellationToken.None);
+
+ // assert
+ var compositeWellOperation = result.SelectMany(o => o.Values.Where(o => o.IdWell == 0)).FirstOrDefault();
+ Assert.NotNull(compositeWellOperation);
+ Assert.Equal(5036, compositeWellOperation.IdCategory);
+ Assert.Equal(3, compositeWellOperation.DurationHours);
+ Assert.Equal(1372, compositeWellOperation.DepthStart);
+ }
+
+ ///
+ /// На вход подаются 3 операции с одинаковыми секциями (id = 31), но разными категориями (ids = 5012, 5083) и ключами скважин (ids = 55, 64)
+ /// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная
+ /// Операция композитной скважины должна содержать:
+ /// данные той операции, которая содержит минимальный duration_hours
+ /// категорию с ключом 5013
+ /// Операции по скважине с ключом 55 должны объединиться в одну,
+ /// при этом их длительность складывается, а depth_start берется минимальный из двух
+ ///
+ ///
+ [Fact]
+ public async Task GetAsync_return_data4()
+ {
+ // arrange
+ wellOperationRepository.GetAsync(Arg.Any(), Arg.Any())
+ .Returns(wellOperations4);
+
+ var idsWell = new List() { 55, 64 };
+
+ // act
+ var result = await service.GetAsync(idsWell, CancellationToken.None);
+
+ // assert
+ var currentWellOperations = result.SelectMany(o => o.Values.Where(o => o.IdWell != 0));
+ var categories = currentWellOperations.Select(o => o.IdCategory).Distinct();
+ Assert.NotNull(categories);
+ Assert.Single(categories);
+ Assert.Equal(5013, categories.First());
+
+ var currentOperationByWell55 = currentWellOperations.Where(o => o.IdWell == 55).FirstOrDefault();
+ Assert.NotNull(currentOperationByWell55);
+ Assert.Equal(15, currentOperationByWell55.DurationHours);
+ Assert.Equal(500, currentOperationByWell55.DepthStart);
+
+ var categoryName = currentWellOperations.Select(o => o.OperationCategoryName).First();
+ Assert.Equal("Подъем КНБК", categoryName);
+
+ var compositeWellOperation = result.SelectMany(o => o.Values.Where(o => o.IdWell == 0)).FirstOrDefault();
+ Assert.NotNull(compositeWellOperation);
+ Assert.Equal(5013, compositeWellOperation.IdCategory);
+ Assert.Equal(5, compositeWellOperation.DurationHours);
+ Assert.Equal(600, compositeWellOperation.DepthStart);
+ }
+
+ ///
+ /// На вход подаются список разных операций с разными ключами скважин (ids = 55, 64)
+ /// Метод возвращает список из 4-х операций в разрезе 3-х скважин: 2 текущие скважины и одна композитная
+ /// Операция композитной скважины должна содержать глубину забоя = 1372
+ ///
+ ///
+ [Fact]
+ public async Task GetAsync_return_data5()
+ {
+ // arrange
+ var wellOperations = new List();
+ wellOperations.AddRange(wellOperations1);
+ wellOperations.AddRange(wellOperations2);
+ wellOperations.AddRange(wellOperations3);
+ wellOperations.AddRange(wellOperations4);
+
+ wellOperationRepository.GetAsync(Arg.Any(), Arg.Any())
+ .Returns(wellOperations);
+
+ var idsWell = new List() { 55, 64 };
+
+ // act
+ var result = await service.GetAsync(idsWell, CancellationToken.None);
+
+ // assert
+ Assert.Equal(4, result.Count());
+
+ var lastOperation = result.Last();
+ var lastOperationComposite = lastOperation[0];
+ Assert.Equal(1372, lastOperationComposite.DepthStart);
+ }
+ }
+}
diff --git a/AsbCloudWebApi/Controllers/ProcessMaps/ProcessMapPlanBaseController.cs b/AsbCloudWebApi/Controllers/ProcessMaps/ProcessMapPlanBaseController.cs
index 45da7f11..49513f2c 100644
--- a/AsbCloudWebApi/Controllers/ProcessMaps/ProcessMapPlanBaseController.cs
+++ b/AsbCloudWebApi/Controllers/ProcessMaps/ProcessMapPlanBaseController.cs
@@ -30,11 +30,11 @@ public abstract class ProcessMapPlanBaseController : ControllerBase
{
private readonly IChangeLogRepository repository;
private readonly IWellService wellService;
- private readonly ParserExcelService parserService;
+ private readonly ParserExcelService parserService;
protected ProcessMapPlanBaseController(IChangeLogRepository repository,
IWellService wellService,
- ParserExcelService parserService)
+ ParserExcelService parserService)
{
this.repository = repository;
this.wellService = wellService;
@@ -213,7 +213,7 @@ public abstract class ProcessMapPlanBaseController : ControllerBase
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task>> Parse(int idWell,
- [Required] IFormFile file,
+ [Required] IFormFile file,
CancellationToken token)
{
await AssertUserHasAccessToWell(idWell, token);
@@ -222,9 +222,8 @@ public abstract class ProcessMapPlanBaseController : ControllerBase
try
{
- var dto = parserService.Parse(stream, IParserOptionsRequest.Empty());
- foreach (var item in dto.Item)
- item.Item.IdWell = idWell;
+ var options = new WellRelatedParserRequest(idWell);
+ var dto = parserService.Parse(stream, options);
return Ok(dto);
}
diff --git a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs
index 344ed707..814bcdcf 100644
--- a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs
+++ b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs
@@ -13,6 +13,7 @@ using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudInfrastructure.Services.Parser;
+using AsbCloudInfrastructure.Services.Trajectory.Parser;
namespace AsbCloudWebApi.Controllers.Trajectory
{
@@ -25,11 +26,11 @@ namespace AsbCloudWebApi.Controllers.Trajectory
public abstract class TrajectoryEditableController : TrajectoryController
where TDto : TrajectoryGeoDto
{
- private readonly ParserExcelService parserService;
+ private readonly TrajectoryParser parserService;
private readonly ITrajectoryEditableRepository trajectoryRepository;
protected TrajectoryEditableController(IWellService wellService,
- ParserExcelService parserService,
+ TrajectoryParser parserService,
TrajectoryExportService trajectoryExportService,
ITrajectoryEditableRepository trajectoryRepository)
: base(wellService, trajectoryExportService, trajectoryRepository)
@@ -78,7 +79,9 @@ namespace AsbCloudWebApi.Controllers.Trajectory
try
{
- var dto = parserService.Parse(stream, IParserOptionsRequest.Empty());
+ var options = new WellRelatedParserRequest(idWell);
+ var dto = parserService.Parse(stream, options);
+
return Ok(dto);
}
catch (FileFormatException ex)
diff --git a/AsbCloudWebApi/Controllers/WellCompositeOperationController.cs b/AsbCloudWebApi/Controllers/WellCompositeOperationController.cs
new file mode 100644
index 00000000..d662e4e8
--- /dev/null
+++ b/AsbCloudWebApi/Controllers/WellCompositeOperationController.cs
@@ -0,0 +1,51 @@
+using AsbCloudApp.Data;
+using AsbCloudApp.Services;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudWebApi.Controllers
+{
+
+ [ApiController]
+ [Authorize]
+ [Route("api/[controller]")]
+ public class WellCompositeOperationController : ControllerBase
+ {
+ private readonly IWellCompositeOperationService wellCompositeOperationService;
+ private readonly IWellService wellService;
+
+ public WellCompositeOperationController(IWellCompositeOperationService wellCompositeOperationService, IWellService wellService)
+ {
+ this.wellCompositeOperationService = wellCompositeOperationService;
+ this.wellService = wellService;
+ }
+
+ [HttpGet]
+ [ProducesResponseType(typeof(IList>), (int)System.Net.HttpStatusCode.OK)]
+ public async Task GetAsync([FromQuery] IEnumerable idsWells, CancellationToken token)
+ {
+ foreach (var idWell in idsWells)
+ if (!await UserHasAccessToWellAsync(idWell, token))
+ return Forbid();
+
+ var result = await wellCompositeOperationService.GetAsync(idsWells, token)
+ .ConfigureAwait(false);
+
+ return Ok(result);
+ }
+
+ protected async Task UserHasAccessToWellAsync(int idWell, CancellationToken token)
+ {
+ var idCompany = User.GetCompanyId();
+ if (idCompany is not null &&
+ await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, idWell, token)
+ .ConfigureAwait(false))
+ return true;
+ return false;
+ }
+ }
+
+}