Merge pull request '#32181043 - Доработка - Композитная скважина Внести отображение полей по горизонтальной оси Back' (#258) from feature/#32181043-add-durationHours-to-compositeWell into dev

Reviewed-on: http://test.digitaldrilling.ru:8080/DDrilling/AsbCloudServer/pulls/258
This commit is contained in:
Никита Фролов 2024-04-17 16:24:26 +05:00
commit ae4611e650
6 changed files with 305 additions and 260 deletions

View File

@ -0,0 +1,26 @@
using AsbCloudApp.Data.WellOperation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AsbCloudApp.Data
{
/// <summary>
/// Хранение операций по композитной скважине
/// и по скважинам, на основе которых была рассчитана композитная скважина
/// </summary>
public class WellCompositeOperationDto
{
/// <summary>
/// Список операций композитной скважины
/// </summary>
public IEnumerable<WellOperationDto> WellOperationsComposite { get; set; } = null!;
/// <summary>
/// Список операций, на основе которых были рассчитаны операции по композитной скважине
/// </summary>
public IEnumerable<WellCompositeOperationSourceDto> WellCompositeSourceOperations { get; set; } = null!;
}
}

View File

@ -0,0 +1,21 @@
using AsbCloudApp.Data.WellOperation;
using System.Collections.Generic;
namespace AsbCloudApp.Data
{
/// <summary>
/// Операции по скважине, по которой рассчитывается композитная скважина
/// </summary>
public class WellCompositeOperationSourceDto
{
/// <summary>
/// Скважина
/// </summary>
public WellDto Well { get; set; } = null!;
/// <summary>
/// Операции по скважине
/// </summary>
public IEnumerable<WellOperationDto> Operations { get; set; } = null!;
}
}

View File

@ -1,7 +1,7 @@
using System.Collections.Generic; using AsbCloudApp.Data;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data.WellOperation;
namespace AsbCloudApp.Services namespace AsbCloudApp.Services
{ {
@ -16,6 +16,6 @@ namespace AsbCloudApp.Services
/// <param name="idsWells"></param> /// <param name="idsWells"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<IEnumerable<Dictionary<int, WellOperationDto>>> GetAsync(IEnumerable<int> idsWells, CancellationToken token); Task<WellCompositeOperationDto> GetAsync(IEnumerable<int> idsWells, CancellationToken token);
} }
} }

View File

@ -1,220 +1,242 @@
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data.WellOperation;
namespace AsbCloudInfrastructure.Services namespace AsbCloudInfrastructure.Services;
public class WellCompositeOperationService : IWellCompositeOperationService
{ {
public class WellCompositeOperationService : IWellCompositeOperationService private readonly ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository;
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
private readonly IWellOperationRepository wellOperationRepository;
private readonly IWellService wellService;
/// <summary>
/// Тип секции "Транспортный стол"
/// </summary>
private const int wellSectionTransportTable = 5;
/// <summary>
/// Тип секции "Эксплуатационная колонна"
/// </summary>
private const int wellSectionProductionString = 4;
/// <summary>
/// набор настроек для замены одной категории секции на другую
/// </summary>
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)>()
{ {
private ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository; { (2, 5001) },
private IWellOperationCategoryRepository wellOperationCategoryRepository; { (2, 5003) },
private IWellOperationRepository wellOperationRepository; { (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) }
};
/// <summary> public WellCompositeOperationService(
/// Тип секции "Транспортный стол" ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository,
/// </summary> IWellOperationCategoryRepository wellOperationCategoryRepository,
private const int wellSectionTransportTable = 5; IWellOperationRepository wellOperationRepository,
IWellService wellService)
{
this.wellSectionTypeRepository = wellSectionTypeRepository;
this.wellOperationCategoryRepository = wellOperationCategoryRepository;
this.wellOperationRepository = wellOperationRepository;
this.wellService = wellService;
}
/// <summary> public async Task<WellCompositeOperationDto> GetAsync(IEnumerable<int> idsWells, CancellationToken token)
/// Тип секции "Эксплуатационная колонна" {
/// </summary> var sections = await wellSectionTypeRepository.GetAllAsync(token);
private const int wellSectionProductionString = 4; 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);
/// <summary> var wells = await wellService.GetAsync(new WellRequest { Ids = idsWells }, token);
/// набор настроек для замены одной категории секции на другую var wellsDict = wells.ToDictionary(w => w.Id);
/// </summary>
private static Dictionary<(int, int), int> SettingsForSectionCategoryChange = new Dictionary<(int, int), int>() { var idsWellSectionTypes = WellSectionTypesWithCategories.Select(t => t.IdSectionType).Distinct();
{ (2, 5096), 5013 }, var usedCategories = WellSectionTypesWithCategories.Select(c => c.IdCategory).Distinct();
{ (2, 5008), 5013 },
{ (3, 5096), 5084 }, var wellOperationRequest = new WellOperationRequest(idsWells)
{ (3, 5008), 5084 }, {
{ (3, 5085), 5015 }, OperationCategoryIds = usedCategories,
{ (3, 5014), 5015 }, SectionTypeIds = idsWellSectionTypes,
{ (31, 5014), 5015 }, OperationType = WellOperation.IdOperationTypeFact
{ (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 }
}; };
var operations = await wellOperationRepository.GetAsync(wellOperationRequest, token);
private HashSet<(int IdSectionType, int IdCategory)> WellSectionTypesWithCategories = new HashSet<(int IdSectionType, int IdCategory)>() var operationsForComposite = operations.Select(o => CreateCompositeOperation(o, sectionsDict, categoriesDict));
var wellOperationsWithComposite = new List<Dictionary<int, WellOperationDto>>();
var compositeDepth = 0d;
var compositeDay = 0d;
var result = new WellCompositeOperationDto();
var compositeOperations = new List<WellOperationDto>();
foreach ((int IdSection, int IdCategory) in WellSectionTypesWithCategories)
{ {
{ (2, 5001) }, var filteredByTemplate = operationsForComposite
{ (2, 5003) }, .Where(o => o.IdWellSectionType == IdSection)
{ (2, 5013) }, .Where(o => o.IdCategory == IdCategory);
{ (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) }
};
if (!filteredByTemplate.Any())
continue;
var groupedByWell = filteredByTemplate.GroupBy(o => o.IdWell);
public WellCompositeOperationService( var aggreagtedByWell = groupedByWell.Select(g => new WellOperationDto
ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository, {
IWellOperationCategoryRepository wellOperationCategoryRepository, IdCategory = IdCategory,
IWellOperationRepository wellOperationRepository) IdWell = g.Key,
{ IdWellSectionType = IdSection,
this.wellSectionTypeRepository = wellSectionTypeRepository; DepthStart = g.Min(o => o.DepthStart),
this.wellOperationCategoryRepository = wellOperationCategoryRepository; DurationHours = g.Sum(o => o.DurationHours),
this.wellOperationRepository = wellOperationRepository; OperationCategoryName = g.First().OperationCategoryName,
WellSectionTypeCaption = g.First().WellSectionTypeCaption,
});
var compositeOperation = aggreagtedByWell.OrderBy(o => o.DurationHours).
ThenByDescending(o => o.DepthStart)
.First();
compositeOperation.IdWell = 0;
if (compositeDepth > compositeOperation.DepthStart)
compositeOperation.DepthStart = compositeDepth;
compositeDepth = compositeOperation.DepthStart;
compositeDay += compositeOperation.DurationHours;
compositeOperation.Day = compositeDay;
compositeOperations.Add(compositeOperation);
} }
public async Task<IEnumerable<Dictionary<int, WellOperationDto>>> GetAsync(IEnumerable<int> idsWells, CancellationToken token) var groupedByWellOperations = operations
.GroupBy(o => o.IdWell)
.ToDictionary(o => o.Key, o => o.ToArray())
.Select(o => new WellCompositeOperationSourceDto()
{
Operations = o.Value,
Well = wellsDict[o.Key],
});
result.WellOperationsComposite = compositeOperations;
result.WellCompositeSourceOperations = groupedByWellOperations;
return result;
}
private static WellOperationDto CreateCompositeOperation(
WellOperationDto dto,
IDictionary<int, string> sectionTypes,
IDictionary<int, string> operationCategories)
{
var newDto = dto.Adapt<WellOperationDto>();
if (newDto.IdWellSectionType == wellSectionTransportTable)
{ {
var sections = await wellSectionTypeRepository.GetAllAsync(token); newDto.IdWellSectionType = wellSectionProductionString;
var sectionsDict = sections.ToDictionary(s => s.Id, s => s.Caption); newDto.WellSectionTypeCaption = sectionTypes[newDto.IdWellSectionType];
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 WellOperationRequest(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<Dictionary<int, WellOperationDto>>();
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 WellOperationDto
{
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 WellOperationDto UpdateIdWellSectionAndIdCategory( if ((SettingsForSectionCategoryChange.TryGetValue((newDto.IdWellSectionType, newDto.IdCategory), out int newIdCategory)))
WellOperationDto dto,
IDictionary<int, string> sectionTypes,
IDictionary<int, string> operationCategories)
{ {
if (dto.IdWellSectionType == wellSectionTransportTable) newDto.IdCategory = newIdCategory;
{ newDto.OperationCategoryName = operationCategories[newDto.IdCategory] ?? string.Empty;
dto.IdWellSectionType = wellSectionProductionString;
dto.WellSectionTypeCaption = sectionTypes[dto.IdWellSectionType];
}
if ((SettingsForSectionCategoryChange.TryGetValue((dto.IdWellSectionType, dto.IdCategory), out int newIdCategory)))
{
dto.IdCategory = newIdCategory;
dto.OperationCategoryName = operationCategories[dto.IdCategory] ?? string.Empty;
}
return dto;
} }
return newDto;
} }
} }

View File

@ -1,22 +1,16 @@
using AsbCloudApp.Requests; using AsbCloudApp.Data;
using AsbCloudDb.Model; using AsbCloudApp.Data.WellOperation;
using AsbCloudInfrastructure.Repository; using AsbCloudApp.Repositories;
using AsbCloudInfrastructure.Services.ProcessMaps.Report; using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services; using AsbCloudInfrastructure.Services;
using NSubstitute; using NSubstitute;
using ProtoBuf.Meta;
using SignalRSwaggerGen.Enums;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Xunit;
using AsbCloudApp.Repositories;
using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Services;
namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
{ {
@ -30,8 +24,10 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
= Substitute.For<IWellOperationCategoryRepository>(); = Substitute.For<IWellOperationCategoryRepository>();
private IWellOperationRepository wellOperationRepository private IWellOperationRepository wellOperationRepository
= Substitute.For<IWellOperationRepository>(); = Substitute.For<IWellOperationRepository>();
private IWellService wellService
= Substitute.For<IWellService>();
private readonly static IEnumerable<WellOperationCategoryDto> operationCategories = new List<WellOperationCategoryDto>() private readonly static IEnumerable<WellOperationCategoryDto> operationCategories = new List<WellOperationCategoryDto>()
{ {
@ -61,7 +57,7 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
IdWell = 55, IdWell = 55,
IdWellSectionType = 2, IdWellSectionType = 2,
OperationCategoryName = "Шаблонирование перед спуском", OperationCategoryName = "Шаблонирование перед спуском",
WellSectionTypeCaption = "Направление" WellSectionTypeCaption = "Направление"
}, },
new() new()
{ {
@ -167,15 +163,16 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
.Returns(operationCategories); .Returns(operationCategories);
service = new WellCompositeOperationService( service = new WellCompositeOperationService(
wellSectionTypeRepository, wellSectionTypeRepository,
wellOperationCategoryRepository, wellOperationCategoryRepository,
wellOperationRepository); wellOperationRepository,
wellService);
} }
/// <summary> /// <summary>
/// На вход подаются 2 операции с одинаковыми секциями (id = 2), но разными категориями (ids = 5096, 5008) и ключами скважин (ids = 55, 64) /// На вход подаются 2 операции с одинаковыми секциями (id = 2), но разными категориями (ids = 5096, 5008) и ключами скважин (ids = 55, 64)
/// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная /// Метод возвращает объект с одной композитной операцией и списком операций в разрезе 2-х скважин
/// Операция должна иметь категорию 5013 для всех трех скважин /// Композитная операция должна иметь категорию 5013
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Fact] [Fact]
@ -185,31 +182,25 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
wellOperationRepository.GetAsync(Arg.Any<WellOperationRequest>(), Arg.Any<CancellationToken>()) wellOperationRepository.GetAsync(Arg.Any<WellOperationRequest>(), Arg.Any<CancellationToken>())
.Returns(wellOperations1); .Returns(wellOperations1);
var idsWell = new List<int>() { 55, 64 }; var idsWell = new List<int>() { 55, 64 };
// act // act
var result = await service.GetAsync(idsWell, CancellationToken.None); var result = await service.GetAsync(idsWell, CancellationToken.None);
// assert // assert
var compositeWellOperation = result.SelectMany(o => o.Values.Where(o => o.IdWell == 0)).FirstOrDefault(); var compositeWellOperations = result.WellOperationsComposite;
Assert.NotNull(compositeWellOperation); Assert.Single(compositeWellOperations);
Assert.Equal(5013, compositeWellOperation.IdCategory); Assert.Equal(5013, compositeWellOperations.FirstOrDefault()?.IdCategory);
Assert.Equal(1, compositeWellOperation.DurationHours); Assert.Equal(1, compositeWellOperations.FirstOrDefault()?.DurationHours);
Assert.Equal(84, compositeWellOperation.DepthStart); Assert.Equal(84, compositeWellOperations.FirstOrDefault()?.DepthStart);
var currentWellOperations = result.SelectMany(o => o.Values.Where(o => o.IdWell != 0)); var categoryName = compositeWellOperations.Select(o => o.OperationCategoryName).First();
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); Assert.Equal("Подъем КНБК", categoryName);
} }
/// <summary> /// <summary>
/// На вход подаются 2 операции с одинаковыми секциями (id = 2) и категориями (id = 5003), но разными ключами скважин (ids = 55, 64) /// На вход подаются 2 операции с одинаковыми секциями (id = 2) и категориями (id = 5003), но разными ключами скважин (ids = 55, 64)
/// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная /// Метод возвращает объект с одной композитной операцией и списком операций в разрезе 2-х скважин
/// Операция композитной скважины должна содержать данные той операции, которая содержит минимальный duration_hours /// Операция композитной скважины должна содержать данные той операции, которая содержит минимальный duration_hours
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
@ -226,16 +217,16 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
var result = await service.GetAsync(idsWell, CancellationToken.None); var result = await service.GetAsync(idsWell, CancellationToken.None);
// assert // assert
var compositeWellOperation = result.SelectMany(o => o.Values.Where(o => o.IdWell == 0)).FirstOrDefault(); var compositeWellOperations = result.WellOperationsComposite;
Assert.NotNull(compositeWellOperation); Assert.Single(compositeWellOperations);
Assert.Equal(5003, compositeWellOperation.IdCategory); Assert.Equal(5003, compositeWellOperations.FirstOrDefault()!.IdCategory);
Assert.Equal(1.5, compositeWellOperation.DurationHours); Assert.Equal(1.5, compositeWellOperations.FirstOrDefault()!.DurationHours);
Assert.Equal(10, compositeWellOperation.DepthStart); Assert.Equal(10, compositeWellOperations.FirstOrDefault()!.DepthStart);
} }
/// <summary> /// <summary>
/// На вход подаются 2 операции с одинаковыми секциями (id = 3) и категориями (id = 5036), но разными ключами скважин (ids = 55, 64) /// На вход подаются 2 операции с одинаковыми секциями (id = 3) и категориями (id = 5036), но разными ключами скважин (ids = 55, 64)
/// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная /// Метод возвращает объект с одной композитной операцией и списком операций в разрезе 2-х скважин
/// Операция композитной скважины должна содержать данные той операции, которая содержит минимальный duration_hours /// Операция композитной скважины должна содержать данные той операции, которая содержит минимальный duration_hours
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
@ -252,21 +243,19 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
var result = await service.GetAsync(idsWell, CancellationToken.None); var result = await service.GetAsync(idsWell, CancellationToken.None);
// assert // assert
var compositeWellOperation = result.SelectMany(o => o.Values.Where(o => o.IdWell == 0)).FirstOrDefault(); var compositeWellOperations = result.WellOperationsComposite;
Assert.NotNull(compositeWellOperation); Assert.Single(compositeWellOperations);
Assert.Equal(5036, compositeWellOperation.IdCategory); Assert.Equal(5036, compositeWellOperations.FirstOrDefault()!.IdCategory);
Assert.Equal(3, compositeWellOperation.DurationHours); Assert.Equal(3, compositeWellOperations.FirstOrDefault()!.DurationHours);
Assert.Equal(1372, compositeWellOperation.DepthStart); Assert.Equal(1372, compositeWellOperations.FirstOrDefault()!.DepthStart);
} }
/// <summary> /// <summary>
/// На вход подаются 3 операции с одинаковыми секциями (id = 31), но разными категориями (ids = 5012, 5083) и ключами скважин (ids = 55, 64) /// На вход подаются 3 операции с одинаковыми секциями (id = 31), но разными категориями (ids = 5012, 5083) и ключами скважин (ids = 55, 64)
/// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная /// Метод возвращает объект с одной композитной операцией и списком операций в разрезе 2-х скважин
/// Операция композитной скважины должна содержать: /// Операция композитной скважины должна содержать:
/// данные той операции, которая содержит минимальный duration_hours /// данные той операции, которая содержит минимальный duration_hours
/// категорию с ключом 5013 /// категорию с ключом 5013
/// Операции по скважине с ключом 55 должны объединиться в одну,
/// при этом их длительность складывается, а depth_start берется минимальный из двух
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Fact] [Fact]
@ -282,30 +271,16 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
var result = await service.GetAsync(idsWell, CancellationToken.None); var result = await service.GetAsync(idsWell, CancellationToken.None);
// assert // assert
var currentWellOperations = result.SelectMany(o => o.Values.Where(o => o.IdWell != 0)); var compositeWellOperations = result.WellOperationsComposite;
var categories = currentWellOperations.Select(o => o.IdCategory).Distinct(); Assert.Single(compositeWellOperations);
Assert.NotNull(categories); Assert.Equal(5013, compositeWellOperations.FirstOrDefault()!.IdCategory);
Assert.Single(categories); Assert.Equal(5, compositeWellOperations.FirstOrDefault()!.DurationHours);
Assert.Equal(5013, categories.First()); Assert.Equal(600, compositeWellOperations.FirstOrDefault()!.DepthStart);
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);
} }
/// <summary> /// <summary>
/// На вход подаются список разных операций с разными ключами скважин (ids = 55, 64) /// На вход подаются список разных операций с разными ключами скважин (ids = 55, 64)
/// Метод возвращает список из 4-х операций в разрезе 3-х скважин: 2 текущие скважины и одна композитная /// Метод возвращает объект с одной композитной операцией и списком операций в разрезе 2-х скважин
/// Операция композитной скважины должна содержать глубину забоя = 1372 /// Операция композитной скважины должна содержать глубину забоя = 1372
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
@ -328,10 +303,11 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
var result = await service.GetAsync(idsWell, CancellationToken.None); var result = await service.GetAsync(idsWell, CancellationToken.None);
// assert // assert
Assert.Equal(4, result.Count()); var wellOperationsCount = result.WellCompositeSourceOperations
.SelectMany(o => o.Operations).Count();
Assert.Equal(wellOperations.Count(), wellOperationsCount);
var lastOperation = result.Last(); var lastOperationComposite = result.WellOperationsComposite.Last();
var lastOperationComposite = lastOperation[0];
Assert.Equal(1372, lastOperationComposite.DepthStart); Assert.Equal(1372, lastOperationComposite.DepthStart);
} }
} }

View File

@ -25,7 +25,7 @@ namespace AsbCloudWebApi.Controllers
} }
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(IList<IDictionary<int, WellOperationDto>>), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(WellCompositeOperationDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetAsync([FromQuery] IEnumerable<int> idsWells, CancellationToken token) public async Task<IActionResult> GetAsync([FromQuery] IEnumerable<int> idsWells, CancellationToken token)
{ {
foreach (var idWell in idsWells) foreach (var idWell in idsWells)