Merge branch 'dev' into feature/#29885445-detector-flashing

This commit is contained in:
on.nemtina 2024-03-15 14:58:05 +05:00
commit b3f31711b7
66 changed files with 2356 additions and 1396 deletions

View File

@ -10,7 +10,6 @@ public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelat
/// <summary> /// <summary>
/// Id скважины /// Id скважины
/// </summary> /// </summary>
[Range(1, int.MaxValue, ErrorMessage = "Id скважины не может быть меньше 1")]
public int IdWell { get; set; } public int IdWell { get; set; }
/// <summary> /// <summary>

View File

@ -17,7 +17,7 @@ public class ProcessMapPlanDrillingDto : ProcessMapPlanBaseDto
/// Название режима бурения /// Название режима бурения
/// </summary> /// </summary>
public string? Mode { get; set; } public string? Mode { get; set; }
/// <summary> /// <summary>
/// Осевая нагрузка, т план /// Осевая нагрузка, т план
/// </summary> /// </summary>

View File

@ -0,0 +1,39 @@
namespace AsbCloudApp.Data
{
/// Операция на скважине
public class WellOperationDataDto : IWellRelated
{
/// <inheritdoc/>
public int IdWell { get; set; }
/// <summary>
/// id секции скважины
/// </summary>
public int IdWellSectionType { get; set; }
/// <summary>
/// id категории операции
/// </summary>
public int IdCategory { get; set; }
/// <summary>
/// Глубина на начало операции, м
/// </summary>
public double DepthStart { get; set; }
/// <summary>
/// Продолжительность, часы
/// </summary>
public double DurationHours { get; set; }
/// <summary>
/// Наименование секции
/// </summary>
public string WellSectionTypeCaption { get; set; } = string.Empty;
/// <summary>
/// Наименование категории
/// </summary>
public string OperationCategoryName { get; set; } = string.Empty;
}
}

View File

@ -42,6 +42,14 @@ namespace AsbCloudApp.Repositories
/// <returns></returns> /// <returns></returns>
Task<IEnumerable<WellOperationDto>> GetAsync(WellOperationRequest request, CancellationToken token); Task<IEnumerable<WellOperationDto>> GetAsync(WellOperationRequest request, CancellationToken token);
/// <summary>
/// Получить список операций по запросу
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<WellOperationDataDto>> GetAsync(WellsOperationRequest request, CancellationToken token);
/// <summary> /// <summary>
/// Получить страницу списка операций /// Получить страницу списка операций
/// </summary> /// </summary>

View File

@ -0,0 +1,8 @@
namespace AsbCloudApp.Requests.ExportOptions;
/// <summary>
/// Параметры экспорта
/// </summary>
public interface IExportOptionsRequest
{
}

View File

@ -0,0 +1,21 @@
namespace AsbCloudApp.Requests.ExportOptions;
/// <summary>
/// Параметры экспорта
/// </summary>
public class WellRelatedExportRequest : IExportOptionsRequest
{
/// <summary>
/// Конструктор
/// </summary>
/// <param name="idWell">Id скважины</param>
public WellRelatedExportRequest(int idWell)
{
IdWell = idWell;
}
/// <summary>
/// Id скважины
/// </summary>
public int IdWell { get; }
}

View File

@ -0,0 +1,21 @@
namespace AsbCloudApp.Requests.ParserOptions;
/// <summary>
/// Параметры парсинга
/// </summary>
public class WellRelatedParserRequest : IParserOptionsRequest
{
/// <summary>
/// Конструктор
/// </summary>
/// <param name="idWell">Id скважины</param>
public WellRelatedParserRequest(int idWell)
{
IdWell = idWell;
}
/// <summary>
/// Id скважины
/// </summary>
public int IdWell { get; }
}

View File

@ -6,7 +6,7 @@ namespace AsbCloudApp.Requests
/// <summary> /// <summary>
/// параметры для запроса списка операций /// параметры для запроса списка операций
/// </summary> /// </summary>
public class WellOperationRequestBase: RequestBase public class WellOperationRequestBase : RequestBase
{ {
/// <summary> /// <summary>
/// фильтр по дате начала операции /// фильтр по дате начала операции
@ -42,12 +42,40 @@ namespace AsbCloudApp.Requests
/// фильтр по списку id конструкций секции /// фильтр по списку id конструкций секции
/// </summary> /// </summary>
public IEnumerable<int>? SectionTypeIds { get; set; } public IEnumerable<int>? SectionTypeIds { get; set; }
/// <summary>
/// Параметры для запроса списка операций.
/// Базовый конструктор
/// </summary>
public WellOperationRequestBase()
{ }
/// <summary>
/// Параметры для запроса списка операций.
/// Копирующий конструктор
/// </summary>
/// <param name="request"></param>
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;
}
} }
/// <summary> /// <summary>
/// Параметры для запроса списка операций (с id скважины) /// Параметры для запроса списка операций (с id скважины)
/// </summary> /// </summary>
public class WellOperationRequest: WellOperationRequestBase public class WellOperationRequest : WellOperationRequestBase
{ {
/// <summary> /// <summary>
/// id скважины /// id скважины
@ -57,7 +85,7 @@ namespace AsbCloudApp.Requests
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public WellOperationRequest(){} public WellOperationRequest() { }
/// <summary> /// <summary>
/// копирующий конструктор /// копирующий конструктор
@ -65,21 +93,20 @@ namespace AsbCloudApp.Requests
/// <param name="request"></param> /// <param name="request"></param>
/// <param name="idWell"></param> /// <param name="idWell"></param>
public WellOperationRequest(WellOperationRequestBase request, int idWell) public WellOperationRequest(WellOperationRequestBase request, int idWell)
:base(request)
{ {
this.IdWell = idWell; 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;
} }
} }
/// <summary>
/// Параметры для запроса списка операций (с массивом id скважин)
/// </summary>
public class WellsOperationRequest : WellOperationRequestBase
{
/// <summary>
/// ids скважин
/// </summary>
public IEnumerable<int> IdsWell { get; set; } = null!;
}
} }

View File

@ -0,0 +1,21 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Requests.ExportOptions;
namespace AsbCloudApp.Services;
/// <summary>
/// Экспорт данных
/// </summary>
public interface IExportService<in TOptions>
where TOptions : IExportOptionsRequest
{
/// <summary>
/// Экспортировать данные
/// </summary>
/// <param name="options"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<(string FileName, Stream File)> ExportAsync(TOptions options, CancellationToken token);
}

View File

@ -8,8 +8,10 @@ namespace AsbCloudApp.Services;
/// Сервис парсинга /// Сервис парсинга
/// </summary> /// </summary>
/// <typeparam name="TDto"></typeparam> /// <typeparam name="TDto"></typeparam>
public interface IParserService<TDto> : IParserService /// <typeparam name="TOptions"></typeparam>
public interface IParserService<TDto, in TOptions>
where TDto : class, IId where TDto : class, IId
where TOptions : IParserOptionsRequest
{ {
/// <summary> /// <summary>
/// Распарсить файл /// Распарсить файл
@ -17,19 +19,11 @@ public interface IParserService<TDto> : IParserService
/// <param name="file"></param> /// <param name="file"></param>
/// <param name="options"></param> /// <param name="options"></param>
/// <returns></returns> /// <returns></returns>
ParserResultDto<TDto> Parse<TOptions>(Stream file, TOptions options) ParserResultDto<TDto> Parse(Stream file, TOptions options);
where TOptions : IParserOptionsRequest;
/// <summary> /// <summary>
/// Получение шаблона для заполнения /// Получение шаблона для заполнения
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Stream GetTemplateFile(); Stream GetTemplateFile();
}
/// <summary>
/// Сервис парсинга(интерфейс маркер)
/// </summary>
public interface IParserService
{
} }

View File

@ -0,0 +1,21 @@
using AsbCloudApp.Data;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Services
{
/// <summary>
/// Интерфейс для вычисления композитной скважины
/// </summary>
public interface IWellCompositeOperationService
{
/// <summary>
/// Получение данных для построения композитной скважины
/// </summary>
/// <param name="idsWells"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<List<Dictionary<int, WellOperationDataDto>>> GetAsync(IEnumerable<int> idsWells, CancellationToken token);
}
}

View File

@ -154,13 +154,19 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
var hasOscillation = EnabledSubsystemsFlags.AutoOscillation.HasEnabledSubsystems(operation.EnabledSubsystems); var hasOscillation = EnabledSubsystemsFlags.AutoOscillation.HasEnabledSubsystems(operation.EnabledSubsystems);
var aggregatedValues = CalcAggregate(span); var aggregatedValues = CalcAggregate(span);
var dateStart = span[0].DateTime;
var dateEnd = span[^1].DateTime;
var depthStart = span[0].WellDepth;
var depthEnd = span[^1].WellDepth;
var speed = ((depthEnd - depthStart) / (dateEnd - dateStart).TotalHours);
var processMapDrillingCacheItem = new DataSaubStatDto var processMapDrillingCacheItem = new DataSaubStatDto
{ {
DateStart = operation.DateStart, DateStart = dateStart,
DateEnd = operation.DateEnd, DateEnd = dateEnd,
DepthStart = operation.DepthStart, DepthStart = depthStart,
DepthEnd = operation.DepthEnd, DepthEnd = depthEnd,
Speed = (operation.DepthEnd - operation.DepthStart) / ((operation.DateEnd - operation.DateStart).TotalHours), Speed = speed,
BlockSpeedSp = span[0].BlockSpeedSp, BlockSpeedSp = span[0].BlockSpeedSp,
Pressure = aggregatedValues.Pressure, Pressure = aggregatedValues.Pressure,
PressureIdle = span[0].PressureIdle, PressureIdle = span[0].PressureIdle,

View File

@ -45,6 +45,7 @@ using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using AsbCloudInfrastructure.Services.ProcessMapPlan.Export;
namespace AsbCloudInfrastructure namespace AsbCloudInfrastructure
{ {
@ -161,9 +162,6 @@ namespace AsbCloudInfrastructure
services.AddScoped<IWellService, WellService>(); services.AddScoped<IWellService, WellService>();
services.AddTransient<IWellOperationImportService, WellOperationImportService>(); services.AddTransient<IWellOperationImportService, WellOperationImportService>();
services.AddTransient<IProcessMapReportDrillingExportService, ProcessMapReportDataSaubStatExportService>(); services.AddTransient<IProcessMapReportDrillingExportService, ProcessMapReportDataSaubStatExportService>();
services.AddTransient<TrajectoryPlanExportService>();
services.AddTransient<TrajectoryFactManualExportService>();
services.AddTransient<TrajectoryFactNnbExportService>();
services.AddTransient<IWellOperationRepository, WellOperationRepository>(); services.AddTransient<IWellOperationRepository, WellOperationRepository>();
services.AddTransient<IDailyReportService, DailyReportService>(); services.AddTransient<IDailyReportService, DailyReportService>();
services.AddTransient<IDetectedOperationService, DetectedOperationService>(); services.AddTransient<IDetectedOperationService, DetectedOperationService>();
@ -198,6 +196,7 @@ namespace AsbCloudInfrastructure
services.AddTransient<ICrudRepository<NotificationCategoryDto>, CrudCacheRepositoryBase<NotificationCategoryDto, services.AddTransient<ICrudRepository<NotificationCategoryDto>, CrudCacheRepositoryBase<NotificationCategoryDto,
NotificationCategory>>(); NotificationCategory>>();
services.AddTransient<IDrillTestRepository, DrillTestRepository>(); services.AddTransient<IDrillTestRepository, DrillTestRepository>();
services.AddTransient<IWellCompositeOperationService, WellCompositeOperationService>();
// admin crud services: // admin crud services:
services.AddTransient<ICrudRepository<TelemetryDto>, CrudCacheRepositoryBase<TelemetryDto, Telemetry>>(s => services.AddTransient<ICrudRepository<TelemetryDto>, CrudCacheRepositoryBase<TelemetryDto, Telemetry>>(s =>
@ -294,7 +293,14 @@ namespace AsbCloudInfrastructure
services.AddTransient<TrajectoryFactManualParser>(); services.AddTransient<TrajectoryFactManualParser>();
services.AddTransient<ProcessMapPlanDrillingParser>(); services.AddTransient<ProcessMapPlanDrillingParser>();
services.AddTransient<ProcessMapPlanReamParser>(); services.AddTransient<ProcessMapPlanReamParser>();
services.AddTransient<TrajectoryPlanExportService>();
services.AddTransient<TrajectoryFactManualExportService>();
services.AddTransient<TrajectoryFactNnbExportService>();
services.AddTransient<ProcessMapPlanDrillingExportService>();
services.AddTransient<ProcessMapPlanReamExportService>();
return services; return services;
} }
} }

View File

@ -28,6 +28,7 @@ public class ProcessMapPlanBaseRepository<TDto, TEntity> : ChangeLogRepositoryAb
.Include(e => e.Author) .Include(e => e.Author)
.Include(e => e.Editor) .Include(e => e.Editor)
.Include(e => e.Well) .Include(e => e.Well)
.Include(e => e.WellSectionType)
.Where(e => e.IdWell == request.IdWell); .Where(e => e.IdWell == request.IdWell);
if (request.IdWellSectionType.HasValue) if (request.IdWellSectionType.HasValue)
@ -56,4 +57,11 @@ public class ProcessMapPlanBaseRepository<TDto, TEntity> : ChangeLogRepositoryAb
var offset = TimeSpan.FromHours(timezone.Hours); var offset = TimeSpan.FromHours(timezone.Hours);
return offset; return offset;
} }
protected override TDto Convert(TEntity entity, TimeSpan offset)
{
var dto = base.Convert(entity, offset);
dto.Section = entity.WellSectionType.Caption;
return dto;
}
} }

View File

@ -205,6 +205,17 @@ public class WellOperationRepository : IWellOperationRepository
return dtos.Select(Convert); return dtos.Select(Convert);
} }
public async Task<IEnumerable<WellOperationDataDto>> GetAsync(
WellsOperationRequest request,
CancellationToken token)
{
var query = BuildQuery(request)
.AsNoTracking();
var dtos = await query.ToArrayAsync(token);
return dtos;
}
/// <inheritdoc/> /// <inheritdoc/>
public async Task<PaginationContainer<WellOperationDto>> GetPageAsync( public async Task<PaginationContainer<WellOperationDto>> GetPageAsync(
WellOperationRequest request, WellOperationRequest request,
@ -385,7 +396,6 @@ public class WellOperationRepository : IWellOperationRepository
.Include(s => s.OperationCategory) .Include(s => s.OperationCategory)
.Where(o => o.IdWell == request.IdWell); .Where(o => o.IdWell == request.IdWell);
if (request.OperationType.HasValue) if (request.OperationType.HasValue)
query = query.Where(e => e.IdType == request.OperationType.Value); query = query.Where(e => e.IdType == request.OperationType.Value);
@ -420,6 +430,7 @@ public class WellOperationRepository : IWellOperationRepository
.Where(subOp => subOp.IdType == 1) .Where(subOp => subOp.IdType == 1)
.Where(subOp => WellOperationCategory.NonProductiveTimeSubIds.Contains(subOp.IdCategory)); .Where(subOp => WellOperationCategory.NonProductiveTimeSubIds.Contains(subOp.IdCategory));
// TODO: Вынести query.Select из метода BuildQuery
var dtos = query.Select(o => new WellOperationDto var dtos = query.Select(o => new WellOperationDto
{ {
Id = o.Id, Id = o.Id,
@ -472,6 +483,50 @@ public class WellOperationRepository : IWellOperationRepository
return dtos.AsNoTracking(); return dtos.AsNoTracking();
} }
/// <summary>
/// Получение данных по запросу
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
private IQueryable<WellOperationDataDto> 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) private WellOperationDto Convert(WellOperationDto dto)
{ {
var timezone = wellService.GetTimezone(dto.IdWell); var timezone = wellService.GetTimezone(dto.IdWell);

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using ClosedXML.Excel; using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.Parser; namespace AsbCloudInfrastructure.Services.ExcelServices;
public class Cell public class Cell
{ {

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using ClosedXML.Excel;
using Mapster;
namespace AsbCloudInfrastructure.Services.ExcelServices;
public abstract class ExportExcelService<TDto, TOptions> : IExportService<TOptions>
where TOptions : IExportOptionsRequest
{
protected abstract ITemplateParameters TemplateParameters { get; }
protected abstract Task<string> BuildFileNameAsync(TOptions options, CancellationToken token);
protected abstract Task<IEnumerable<TDto>> GetDtosAsync(TOptions options, CancellationToken token);
public async Task<(string FileName, Stream File)> ExportAsync(TOptions options, CancellationToken token)
{
var dtos = await GetDtosAsync(options, token);
var fileName = await BuildFileNameAsync(options, token);
var file = BuildFile(dtos);
return (fileName, file);
}
private Stream BuildFile(IEnumerable<TDto> dtos)
{
using var template = GetTemplateFile();
using var workbook = new XLWorkbook(template);
AddDtosToWorkbook(workbook, dtos);
var memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { });
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
private void AddDtosToWorkbook(XLWorkbook workbook, IEnumerable<TDto> dtos)
{
var dtosToArray = dtos.ToArray();
if (!dtosToArray.Any())
return;
var sheet = workbook.GetWorksheet(TemplateParameters.SheetName);
for (var i = 0; i < dtosToArray.Length; i++)
{
var row = sheet.Row(1 + i + TemplateParameters.HeaderRowsCount);
AddRow(row, dtosToArray[i]);
}
}
private void AddRow(IXLRow xlRow, TDto dto)
{
var properties = dto.Adapt<IDictionary<string, object>>();
foreach (var (name, cellValue) in properties)
{
if (TemplateParameters.Cells.TryGetValue(name, out var cell))
xlRow.Cell(cell.ColumnNumber).SetCellValue(cellValue);
}
}
private Stream GetTemplateFile() =>
Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateParameters.FileName)
?? throw new ArgumentNullException($"Файл '{TemplateParameters.FileName}' не найден");
}

View File

@ -7,39 +7,34 @@ using System.Reflection;
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Requests.ParserOptions;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using ClosedXML.Excel; using ClosedXML.Excel;
using Mapster; using Mapster;
namespace AsbCloudInfrastructure.Services.Parser; namespace AsbCloudInfrastructure.Services.ExcelServices;
public abstract class ParserExcelService<TDto> : IParserService<TDto> public abstract class ParserExcelService<TDto, TOptions> : IParserService<TDto, TOptions>
where TDto : class, IValidatableObject, IId where TDto : class, IValidatableObject, IId
where TOptions : IParserOptionsRequest
{ {
protected abstract string SheetName { get; } protected abstract ITemplateParameters TemplateParameters { get; }
protected virtual int HeaderRowsCount => 0; public virtual ParserResultDto<TDto> Parse(Stream file, TOptions options)
protected abstract string TemplateFileName { get; }
protected abstract IDictionary<string, Cell> Cells { get; }
public virtual ParserResultDto<TDto> Parse<TOptions>(Stream file, TOptions options)
where TOptions : IParserOptionsRequest
{ {
using var workbook = new XLWorkbook(file); using var workbook = new XLWorkbook(file);
var sheet = workbook.GetWorksheet(SheetName); var sheet = workbook.GetWorksheet(TemplateParameters.SheetName);
var dtos = ParseExcelSheet(sheet); var dtos = ParseExcelSheet(sheet);
return dtos; return dtos;
} }
public virtual Stream GetTemplateFile() => public virtual Stream GetTemplateFile() =>
Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateFileName) Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateParameters.FileName)
?? throw new ArgumentNullException($"Файл '{TemplateFileName}' не найден"); ?? throw new ArgumentNullException($"Файл '{TemplateParameters.FileName}' не найден");
protected virtual IDictionary<string, object?> ParseRow(IXLRow xlRow) protected virtual IDictionary<string, object?> ParseRow(IXLRow xlRow)
{ {
var cells = Cells.ToDictionary(x => x.Key, x => var cells = TemplateParameters.Cells.ToDictionary(x => x.Key, x =>
{ {
var columnNumber = x.Value.ColumnNumber; var columnNumber = x.Value.ColumnNumber;
var xlCell = xlRow.Cell(columnNumber); var xlCell = xlRow.Cell(columnNumber);
@ -72,7 +67,7 @@ public abstract class ParserExcelService<TDto> : IParserService<TDto>
return validDto; return validDto;
} }
var columnsDict = Cells.ToDictionary(x => x.Key, x => x.Value.ColumnNumber); var columnsDict = TemplateParameters.Cells.ToDictionary(x => x.Key, x => x.Value.ColumnNumber);
var invalidDto = new ValidationResultDto<TDto> var invalidDto = new ValidationResultDto<TDto>
{ {
@ -84,7 +79,10 @@ public abstract class ParserExcelService<TDto> : IParserService<TDto>
{ {
var columnNumber = columnsDict[m]; var columnNumber = columnsDict[m];
var errorMessage = v.ErrorMessage; var errorMessage = v.ErrorMessage;
var warningMessage = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, rowNumber, columnNumber, var warningMessage = string.Format(XLExtentions.ProblemDetailsTemplate,
TemplateParameters.SheetName,
rowNumber,
columnNumber,
errorMessage); errorMessage);
var warning = new ValidationResult(warningMessage, new[] { m }); var warning = new ValidationResult(warningMessage, new[] { m });
return warning; return warning;
@ -96,7 +94,7 @@ public abstract class ParserExcelService<TDto> : IParserService<TDto>
protected virtual ParserResultDto<TDto> ParseExcelSheet(IXLWorksheet sheet) protected virtual ParserResultDto<TDto> ParseExcelSheet(IXLWorksheet sheet)
{ {
var count = sheet.RowsUsed().Count() - HeaderRowsCount; var count = sheet.RowsUsed().Count() - TemplateParameters.HeaderRowsCount;
if (count <= 0) if (count <= 0)
return new ParserResultDto<TDto>(); return new ParserResultDto<TDto>();
@ -105,7 +103,7 @@ public abstract class ParserExcelService<TDto> : IParserService<TDto>
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
var xlRow = sheet.Row(1 + i + HeaderRowsCount); var xlRow = sheet.Row(1 + i + TemplateParameters.HeaderRowsCount);
var rowNumber = xlRow.RowNumber(); var rowNumber = xlRow.RowNumber();
try try

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace AsbCloudInfrastructure.Services.ExcelServices.Templates;
public interface ITemplateParameters
{
string SheetName { get; }
int HeaderRowsCount { get; }
string FileName { get; }
IDictionary<string, Cell> Cells { get; }
}

View File

@ -0,0 +1,35 @@
using System.Collections.Generic;
using AsbCloudApp.Data.ProcessMaps;
namespace AsbCloudInfrastructure.Services.ExcelServices.Templates.ProcessMapPlanTemplates;
public class ProcessMapPlanDrillingTemplate : ITemplateParameters
{
public string SheetName => "План";
public int HeaderRowsCount => 2;
public string FileName => "ProcessMapPlanDrillingTemplate.xlsx";
public IDictionary<string, Cell> Cells => new Dictionary<string, Cell>
{
{ nameof(ProcessMapPlanDrillingDto.Section), new Cell(1, typeof(string)) },
{ nameof(ProcessMapPlanDrillingDto.Mode), new Cell(2, typeof(string)) },
{ nameof(ProcessMapPlanDrillingDto.DepthStart), new Cell(3, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.DepthEnd), new Cell(4, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.DeltaPressurePlan), new Cell(5, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.DeltaPressureLimitMax), new Cell(6, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.AxialLoadPlan), new Cell(7, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.AxialLoadLimitMax), new Cell(8, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.TopDriveTorquePlan), new Cell(9, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.TopDriveTorqueLimitMax), new Cell(10, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.TopDriveSpeedPlan), new Cell(11, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.TopDriveSpeedLimitMax), new Cell(12, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.FlowPlan), new Cell(13, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.FlowLimitMax), new Cell(14, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.RopPlan), new Cell(15, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.UsageSaub), new Cell(16, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.UsageSpin), new Cell(17, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.Comment), new Cell(18, typeof(string)) }
};
}

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
using AsbCloudApp.Data.ProcessMaps;
namespace AsbCloudInfrastructure.Services.ExcelServices.Templates.ProcessMapPlanTemplates;
public class ProcessMapPlanReamTemplate : ITemplateParameters
{
public string SheetName => "План";
public int HeaderRowsCount => 1;
public string FileName => "ProcessMapPlanReamTemplate.xlsx";
public IDictionary<string, Cell> Cells => new Dictionary<string, Cell>
{
{ nameof(ProcessMapPlanReamDto.Section), new Cell(1, 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.SpinDownward), new Cell(6, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.SpeedUpward), new Cell(7, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.SpeedDownward), new Cell(8, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.SetpointDrag), new Cell(9, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.SetpointTight), new Cell(10, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.Pressure), new Cell(11, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.Torque), new Cell(12, typeof(double)) },
};
}

View File

@ -0,0 +1,23 @@
using System.Collections.Generic;
using AsbCloudApp.Data.Trajectory;
namespace AsbCloudInfrastructure.Services.ExcelServices.Templates.TrajectoryTemplates;
public class TrajectoryFactManualTemplate : ITemplateParameters
{
public string SheetName => "Фактическая траектория";
public int HeaderRowsCount => 2;
public string FileName => "TrajectoryFactManualTemplate.xlsx";
public IDictionary<string, Cell> Cells => new Dictionary<string, Cell>
{
{ nameof(TrajectoryGeoFactDto.WellboreDepth), new Cell(1, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.ZenithAngle), new Cell(2, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.AzimuthGeo), new Cell(3, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.AzimuthMagnetic), new Cell(4, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.VerticalDepth), new Cell(5, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.Comment), new Cell(6, typeof(string)) }
};
}

View File

@ -0,0 +1,23 @@
using System.Collections.Generic;
using AsbCloudApp.Data.Trajectory;
namespace AsbCloudInfrastructure.Services.ExcelServices.Templates.TrajectoryTemplates;
public class TrajectoryFactNnbTemplate : ITemplateParameters
{
public string SheetName => "Фактическая ннб-траектория";
public int HeaderRowsCount => 2;
public string FileName => "TrajectoryFactNnbTemplate.xlsx";
public IDictionary<string, Cell> Cells => new Dictionary<string, Cell>
{
{ nameof(TrajectoryGeoFactDto.WellboreDepth), new Cell(1, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.ZenithAngle), new Cell(2, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.AzimuthGeo), new Cell(3, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.AzimuthMagnetic), new Cell(4, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.VerticalDepth), new Cell(5, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.Comment), new Cell(6, typeof(string)) }
};
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using AsbCloudApp.Data.Trajectory;
namespace AsbCloudInfrastructure.Services.ExcelServices.Templates.TrajectoryTemplates;
public class TrajectoryPlanTemplate : ITemplateParameters
{
public string SheetName => "Плановая траектория";
public int HeaderRowsCount => 2;
public string FileName => "TrajectoryPlanTemplate.xlsx";
public IDictionary<string, Cell> Cells => new Dictionary<string, Cell>
{
{ nameof(TrajectoryGeoPlanDto.WellboreDepth), new Cell(1, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.ZenithAngle), new Cell(2, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.AzimuthGeo), new Cell(3, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.AzimuthMagnetic), new Cell(4, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.VerticalDepth), new Cell(5, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.Radius), new Cell(6, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.Comment), new Cell(7, typeof(string)) }
};
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using AsbCloudInfrastructure.Services.ExcelServices.Templates.ProcessMapPlanTemplates;
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Export;
public class ProcessMapPlanDrillingExportService : ProcessMapPlanExportService<ProcessMapPlanDrillingDto>
{
public ProcessMapPlanDrillingExportService(
IChangeLogRepository<ProcessMapPlanDrillingDto, ProcessMapPlanBaseRequestWithWell> processMapPlanRepository,
IWellService wellService)
: base(processMapPlanRepository, wellService)
{
}
protected override ITemplateParameters TemplateParameters => new ProcessMapPlanDrillingTemplate();
protected override async Task<string> BuildFileNameAsync(WellRelatedExportRequest options, CancellationToken token)
{
var caption = await wellService.GetWellCaptionByIdAsync(options.IdWell, token);
return $"{caption}_РТК_План_бурение.xlsx";
}
protected override async Task<IEnumerable<ProcessMapPlanDrillingDto>> GetDtosAsync(WellRelatedExportRequest options,
CancellationToken token)
{
var dtos = await base.GetDtosAsync(options, token);
var dtosWithMode = dtos.Select(dto =>
{
dto.Mode = dto.IdMode switch
{
1 => "Ротор",
2 => "Слайд",
_ => throw new ArgumentOutOfRangeException()
};
return dto;
});
return dtosWithMode;
}
}

View File

@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.ExcelServices;
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Export;
public abstract class ProcessMapPlanExportService<TDto> : ExportExcelService<TDto, WellRelatedExportRequest>
where TDto : ChangeLogAbstract
{
protected readonly IWellService wellService;
private readonly IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> processMapPlanRepository;
protected ProcessMapPlanExportService(IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> processMapPlanRepository,
IWellService wellService)
{
this.processMapPlanRepository = processMapPlanRepository;
this.wellService = wellService;
}
protected override async Task<IEnumerable<TDto>> GetDtosAsync(WellRelatedExportRequest options, CancellationToken token)
{
var request = new ProcessMapPlanBaseRequestWithWell(options.IdWell);
var dtos = await processMapPlanRepository.Get(request, token);
return dtos;
}
}

View File

@ -0,0 +1,30 @@
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using AsbCloudInfrastructure.Services.ExcelServices.Templates.ProcessMapPlanTemplates;
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Export;
public class ProcessMapPlanReamExportService : ProcessMapPlanExportService<ProcessMapPlanReamDto>
{
protected override ITemplateParameters TemplateParameters => new ProcessMapPlanReamTemplate();
public ProcessMapPlanReamExportService(
IChangeLogRepository<ProcessMapPlanReamDto, ProcessMapPlanBaseRequestWithWell> processMapPlanRepository,
IWellService wellService)
: base(processMapPlanRepository, wellService)
{
}
protected override async Task<string> BuildFileNameAsync(WellRelatedExportRequest options, CancellationToken token)
{
var caption = await wellService.GetWellCaptionByIdAsync(options.IdWell, token);
return $"{caption}_РТК_План_проработка.xlsx";
}
}

View File

@ -2,50 +2,21 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps; using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudInfrastructure.Services.Parser; using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using Microsoft.Extensions.DependencyInjection; using AsbCloudInfrastructure.Services.ExcelServices.Templates.ProcessMapPlanTemplates;
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
public class ProcessMapPlanDrillingParser : ProcessMapPlanParser<ProcessMapPlanDrillingDto> public class ProcessMapPlanDrillingParser : ProcessMapPlanParser<ProcessMapPlanDrillingDto>
{ {
private readonly IEnumerable<WellSectionTypeDto> sections;
public ProcessMapPlanDrillingParser(IWellOperationRepository wellOperationRepository) public ProcessMapPlanDrillingParser(IWellOperationRepository wellOperationRepository)
: base(wellOperationRepository)
{ {
sections = wellOperationRepository.GetSectionTypes();
} }
protected override string SheetName => "План";
protected override string TemplateFileName => "ProcessMapPlanDrillingTemplate.xlsx";
private const int ColumnSection = 1; protected override ITemplateParameters TemplateParameters => new ProcessMapPlanDrillingTemplate();
private const int ColumnMode = 2;
protected override IDictionary<string, Cell> Cells => new Dictionary<string, Cell>
{
{ nameof(ProcessMapPlanDrillingDto.Section), new Cell(ColumnSection, typeof(string)) },
{ nameof(ProcessMapPlanDrillingDto.Mode), new Cell(ColumnMode, typeof(string)) },
{ nameof(ProcessMapPlanDrillingDto.DepthStart), new Cell(3, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.DepthEnd), new Cell(4, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.DeltaPressurePlan), new Cell(5, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.DeltaPressureLimitMax), new Cell(6, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.AxialLoadPlan), new Cell(7, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.AxialLoadLimitMax), new Cell(8, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.TopDriveTorquePlan), new Cell(9, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.TopDriveTorqueLimitMax), new Cell(10, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.TopDriveSpeedPlan), new Cell(11, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.TopDriveSpeedLimitMax), new Cell(12, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.FlowPlan), new Cell(13, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.FlowLimitMax), new Cell(14, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.RopPlan), new Cell(15, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.UsageSaub), new Cell(16, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.UsageSpin), new Cell(17, typeof(double)) },
{ nameof(ProcessMapPlanDrillingDto.Comment), new Cell(18, typeof(string)) }
};
protected override ProcessMapPlanDrillingDto BuildDto(IDictionary<string, object?> row, int rowNumber) protected override ProcessMapPlanDrillingDto BuildDto(IDictionary<string, object?> row, int rowNumber)
{ {
@ -56,7 +27,10 @@ public class ProcessMapPlanDrillingParser : ProcessMapPlanParser<ProcessMapPlanD
if (section is null) if (section is null)
{ {
var message = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, rowNumber, ColumnSection, var message = string.Format(XLExtentions.ProblemDetailsTemplate,
TemplateParameters.SheetName,
rowNumber,
TemplateParameters.Cells[nameof(ProcessMapPlanDrillingDto.Section)],
"Указана некорректная секция"); "Указана некорректная секция");
throw new FileFormatException(message); throw new FileFormatException(message);
} }
@ -65,7 +39,10 @@ public class ProcessMapPlanDrillingParser : ProcessMapPlanParser<ProcessMapPlanD
if (idMode is null) if (idMode is null)
{ {
var message = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, rowNumber, ColumnSection, var message = string.Format(XLExtentions.ProblemDetailsTemplate,
TemplateParameters.SheetName,
rowNumber,
TemplateParameters.Cells[nameof(ProcessMapPlanDrillingDto.Mode)],
"Указан некорректный режим бурения"); "Указан некорректный режим бурения");
throw new FileFormatException(message); throw new FileFormatException(message);
} }

View File

@ -1,13 +1,32 @@
using System; using System.Collections.Generic;
using System.IO;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps; using AsbCloudApp.Data.ProcessMaps;
using AsbCloudInfrastructure.Services.Parser; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudInfrastructure.Services.ExcelServices;
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
public abstract class ProcessMapPlanParser<TDto> : ParserExcelService<TDto> public abstract class ProcessMapPlanParser<TDto> : ParserExcelService<TDto, WellRelatedParserRequest>
where TDto : ProcessMapPlanBaseDto where TDto : ProcessMapPlanBaseDto
{ {
protected override int HeaderRowsCount => 2; protected readonly IEnumerable<WellSectionTypeDto> sections;
protected ProcessMapPlanParser(IWellOperationRepository wellOperationRepository)
{
sections = wellOperationRepository.GetSectionTypes();
}
public override ParserResultDto<TDto> 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) => protected static int? GetIdMode(string? modeName) =>
modeName?.Trim().ToLower() switch modeName?.Trim().ToLower() switch

View File

@ -2,44 +2,21 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps; using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudInfrastructure.Services.Parser; using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using AsbCloudInfrastructure.Services.ExcelServices.Templates.ProcessMapPlanTemplates;
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
public class ProcessMapPlanReamParser : ProcessMapPlanParser<ProcessMapPlanReamDto> public class ProcessMapPlanReamParser : ProcessMapPlanParser<ProcessMapPlanReamDto>
{ {
private readonly IEnumerable<WellSectionTypeDto> sections;
public ProcessMapPlanReamParser(IWellOperationRepository wellOperationRepository) public ProcessMapPlanReamParser(IWellOperationRepository wellOperationRepository)
: base(wellOperationRepository)
{ {
sections = wellOperationRepository.GetSectionTypes();
} }
protected override string SheetName => "План";
protected override string TemplateFileName => "ProcessMapPlanReamTemplate.xlsx";
private const int ColumnSection = 1; protected override ITemplateParameters TemplateParameters => new ProcessMapPlanReamTemplate();
private const int ColumnMode = 2;
protected override IDictionary<string, Cell> Cells => new Dictionary<string, Cell>
{
{ 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)) },
{ nameof(ProcessMapPlanReamDto.SpeedDownward), new Cell(7, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.SpeedUpward), new Cell(8, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.SetpointDrag), new Cell(9, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.SetpointTight), new Cell(10, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.Pressure), new Cell(11, typeof(double)) },
{ nameof(ProcessMapPlanReamDto.Torque), new Cell(12, typeof(double)) },
};
protected override ProcessMapPlanReamDto BuildDto(IDictionary<string, object?> row, int rowNumber) protected override ProcessMapPlanReamDto BuildDto(IDictionary<string, object?> row, int rowNumber)
{ {
@ -50,7 +27,9 @@ public class ProcessMapPlanReamParser : ProcessMapPlanParser<ProcessMapPlanReamD
if (section is null) if (section is null)
{ {
var message = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, rowNumber, ColumnSection, var message = string.Format(XLExtentions.ProblemDetailsTemplate,
TemplateParameters.SheetName, rowNumber,
TemplateParameters.Cells[nameof(ProcessMapPlanReamDto.Section)],
"Указана некорректная секция"); "Указана некорректная секция");
throw new FileFormatException(message); throw new FileFormatException(message);
} }

View File

@ -1,83 +1,27 @@
using AsbCloudApp.Data.Trajectory; using System.Collections.Generic;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using ClosedXML.Excel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudInfrastructure.Services.ExcelServices;
namespace AsbCloudInfrastructure.Services.Trajectory.Export namespace AsbCloudInfrastructure.Services.Trajectory.Export;
public abstract class TrajectoryExportService<TDto> : ExportExcelService<TDto, WellRelatedExportRequest>
where TDto : TrajectoryGeoDto
{ {
public abstract class TrajectoryExportService<T> where T : TrajectoryGeoDto protected readonly IWellService wellService;
{
private readonly IWellService wellService;
private readonly ITrajectoryRepository<T> trajectoryRepository; private readonly ITrajectoryRepository<TDto> trajectoryRepository;
public abstract string templateFileName { get; }
public abstract string usingTemplateFile { get; }
public abstract string sheetName { get; }
public abstract int headerRowsCount { get; }
public TrajectoryExportService(IWellService wellService, ITrajectoryRepository<T> trajectoryRepository) protected TrajectoryExportService(IWellService wellService, ITrajectoryRepository<TDto> trajectoryRepository)
{ {
this.wellService = wellService; this.wellService = wellService;
this.trajectoryRepository = trajectoryRepository; this.trajectoryRepository = trajectoryRepository;
} }
protected abstract void AddCoordinatesToRow(IXLRow row, T trajectory); protected override Task<IEnumerable<TDto>> GetDtosAsync(WellRelatedExportRequest options, CancellationToken token) =>
trajectoryRepository.GetAsync(options.IdWell, token);
public async Task<Stream> ExportAsync(int idWell, CancellationToken token) }
{
var trajectorys = await trajectoryRepository.GetAsync(idWell, token);
return MakeExelFileStream(trajectorys);
}
public async Task<string> GetFileNameAsync(int idWell, CancellationToken token)
{
var caption = await wellService.GetWellCaptionByIdAsync(idWell, token);
return string.Format("{0}_{1}", caption, templateFileName);
}
public Stream GetTemplateFile()
{
var stream = System.Reflection.Assembly.GetExecutingAssembly()
.GetManifestResourceStream($"{usingTemplateFile}.{templateFileName}");
if (stream is null)
throw new Exception($"Область {usingTemplateFile} не содержит файла с названием {templateFileName}");
return stream;
}
private Stream MakeExelFileStream(IEnumerable<T> trajectories)
{
using Stream ecxelTemplateStream = GetTemplateFile();
using var workbook = new XLWorkbook(ecxelTemplateStream);
AddTrajecoryToWorkbook(workbook, trajectories);
MemoryStream memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { });
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
private void AddTrajecoryToWorkbook(XLWorkbook workbook, IEnumerable<T> trajectories)
{
if (trajectories.Any())
{
var sheet = workbook.GetWorksheet(sheetName);
AddTrajecoryToSheet(sheet, trajectories);
}
}
private void AddTrajecoryToSheet(IXLWorksheet sheet, IEnumerable<T> trajectories)
{
var rowList = trajectories.ToList();
for (int i = 0; i < rowList.Count; i++)
{
var row = sheet.Row(1 + i + headerRowsCount);
AddCoordinatesToRow(row, rowList[i]);
}
}
}
}

View File

@ -1,35 +1,28 @@
using AsbCloudApp.Data.Trajectory; using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using ClosedXML.Excel; using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using AsbCloudInfrastructure.Services.ExcelServices.Templates.TrajectoryTemplates;
namespace AsbCloudInfrastructure.Services.Trajectory.Export namespace AsbCloudInfrastructure.Services.Trajectory.Export;
public class TrajectoryFactManualExportService : TrajectoryExportService<TrajectoryGeoFactDto>
{ {
public TrajectoryFactManualExportService(IWellService wellService,
ITrajectoryEditableRepository<TrajectoryGeoFactDto> trajectoryRepository)
: base(wellService, trajectoryRepository)
{
}
public class TrajectoryFactManualExportService : TrajectoryExportService<TrajectoryGeoFactDto> protected override ITemplateParameters TemplateParameters => new TrajectoryFactManualTemplate();
{
public override string templateFileName { get; } = "TrajectoryFactManualTemplate.xlsx"; protected override async Task<string> BuildFileNameAsync(WellRelatedExportRequest options, CancellationToken token)
public override string usingTemplateFile { get; } = "AsbCloudInfrastructure.Services.Trajectory.Templates"; {
public override string sheetName { get; } = "Фактическая траектория"; var caption = await wellService.GetWellCaptionByIdAsync(options.IdWell, token);
public override int headerRowsCount { get; } = 2;
public TrajectoryFactManualExportService(
IWellService wellService,
ITrajectoryEditableRepository<TrajectoryGeoFactDto> factTrajectoryService)
: base(wellService, factTrajectoryService)
{
}
protected override void AddCoordinatesToRow(IXLRow row, TrajectoryGeoFactDto trajectory)
{
row.Cell(1).SetCellValue(trajectory.WellboreDepth);
row.Cell(2).SetCellValue(trajectory.ZenithAngle);
row.Cell(3).SetCellValue(trajectory.AzimuthGeo);
row.Cell(4).SetCellValue(trajectory.AzimuthMagnetic);
row.Cell(5).SetCellValue(trajectory.VerticalDepth);
row.Cell(6).SetCellValue(trajectory.Comment);
}
}
}
return $"{caption}_Фактическая_траектория.xlsx";
}
}

View File

@ -1,35 +1,28 @@
using AsbCloudApp.Data.Trajectory; using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using ClosedXML.Excel; using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using AsbCloudInfrastructure.Services.ExcelServices.Templates.TrajectoryTemplates;
namespace AsbCloudInfrastructure.Services.Trajectory.Export namespace AsbCloudInfrastructure.Services.Trajectory.Export;
public class TrajectoryFactNnbExportService : TrajectoryExportService<TrajectoryGeoFactDto>
{ {
public TrajectoryFactNnbExportService(IWellService wellService,
ITrajectoryNnbRepository trajectoryRepository)
: base(wellService, trajectoryRepository)
{
}
public class TrajectoryFactNnbExportService : TrajectoryExportService<TrajectoryGeoFactDto> protected override ITemplateParameters TemplateParameters => new TrajectoryFactNnbTemplate();
{
public override string templateFileName { get; } = "TrajectoryFactNnbTemplate.xlsx";
public override string usingTemplateFile { get; } = "AsbCloudInfrastructure.Services.Trajectory.Templates";
public override string sheetName { get; } = "Фактическая ннб-траектория";
public override int headerRowsCount { get; } = 2;
public TrajectoryFactNnbExportService( protected override async Task<string> BuildFileNameAsync(WellRelatedExportRequest options, CancellationToken token)
IWellService wellService, {
ITrajectoryNnbRepository nnbTrajectoryService) var caption = await wellService.GetWellCaptionByIdAsync(options.IdWell, token);
: base(wellService, nnbTrajectoryService)
{
}
protected override void AddCoordinatesToRow(IXLRow row, TrajectoryGeoFactDto trajectory)
{
row.Cell(1).SetCellValue(trajectory.WellboreDepth);
row.Cell(2).SetCellValue(trajectory.ZenithAngle);
row.Cell(3).SetCellValue(trajectory.AzimuthGeo);
row.Cell(4).SetCellValue(trajectory.AzimuthMagnetic);
row.Cell(5).SetCellValue(trajectory.VerticalDepth);
row.Cell(6).SetCellValue(trajectory.Comment);
}
}
}
return $"{caption}_Траектория_ННБ.xlsx";
}
}

View File

@ -1,38 +1,28 @@
using AsbCloudApp.Data.Trajectory; using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using ClosedXML.Excel; using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using AsbCloudInfrastructure.Services.ExcelServices.Templates.TrajectoryTemplates;
namespace AsbCloudInfrastructure.Services.Trajectory.Export namespace AsbCloudInfrastructure.Services.Trajectory.Export;
public class TrajectoryPlanExportService : TrajectoryExportService<TrajectoryGeoPlanDto>
{ {
public class TrajectoryPlanExportService : TrajectoryExportService<TrajectoryGeoPlanDto> public TrajectoryPlanExportService(IWellService wellService,
{ ITrajectoryEditableRepository<TrajectoryGeoPlanDto> trajectoryRepository)
/* : base(wellService, trajectoryRepository)
* password for PlannedTrajectoryTemplate.xlsx is Drill2022 {
*/ }
public override string templateFileName { get; } = "TrajectoryPlanTemplate.xlsx";
public override string usingTemplateFile { get; } = "AsbCloudInfrastructure.Services.Trajectory.Templates";
public override string sheetName { get; } = "Плановая траектория";
public override int headerRowsCount { get; } = 2;
public TrajectoryPlanExportService( protected override ITemplateParameters TemplateParameters => new TrajectoryPlanTemplate();
IWellService wellService,
ITrajectoryEditableRepository<TrajectoryGeoPlanDto> trajectoryPlanService)
: base(wellService, trajectoryPlanService)
{
}
protected override void AddCoordinatesToRow(IXLRow row, TrajectoryGeoPlanDto trajectory) protected override async Task<string> BuildFileNameAsync(WellRelatedExportRequest options, CancellationToken token)
{ {
row.Cell(1).SetCellValue(trajectory.WellboreDepth); var caption = await wellService.GetWellCaptionByIdAsync(options.IdWell, token);
row.Cell(2).SetCellValue(trajectory.ZenithAngle);
row.Cell(3).SetCellValue(trajectory.AzimuthGeo);
row.Cell(4).SetCellValue(trajectory.AzimuthMagnetic);
row.Cell(5).SetCellValue(trajectory.VerticalDepth);
row.Cell(6).SetCellValue(trajectory.Radius);
row.Cell(7).SetCellValue(trajectory.Comment);
}
}
}
return $"{caption}_Плановая_Траектория.xlsx";
}
}

View File

@ -1,24 +1,10 @@
using System.Collections.Generic; using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Data.Trajectory; using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using AsbCloudInfrastructure.Services.Parser; using AsbCloudInfrastructure.Services.ExcelServices.Templates.TrajectoryTemplates;
namespace AsbCloudInfrastructure.Services.Trajectory.Parser; namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
public class TrajectoryFactManualParser : ParserExcelService<TrajectoryGeoFactDto> public class TrajectoryFactManualParser : TrajectoryParser<TrajectoryGeoFactDto>
{ {
protected override string SheetName => "Фактическая траектория"; protected override ITemplateParameters TemplateParameters => new TrajectoryFactManualTemplate();
protected override int HeaderRowsCount => 2;
protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx";
protected override IDictionary<string, Cell> Cells => new Dictionary<string, Cell>
{
{ nameof(TrajectoryGeoFactDto.WellboreDepth), new Cell(1, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.ZenithAngle), new Cell(2, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.AzimuthGeo), new Cell(3, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.AzimuthMagnetic), new Cell(4, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.VerticalDepth), new Cell(5, typeof(double)) },
{ nameof(TrajectoryGeoFactDto.Comment), new Cell(6, typeof(string)) }
};
} }

View File

@ -0,0 +1,21 @@
using System.IO;
using AsbCloudApp.Data;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudInfrastructure.Services.ExcelServices;
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
public abstract class TrajectoryParser<TDto> : ParserExcelService<TDto, WellRelatedParserRequest>
where TDto : TrajectoryGeoDto
{
public override ParserResultDto<TDto> Parse(Stream file, WellRelatedParserRequest options)
{
var result = base.Parse(file, options);
foreach (var item in result.Item)
item.Item.IdWell = options.IdWell;
return result;
}
}

View File

@ -1,25 +1,10 @@
using System.Collections.Generic; using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Data.Trajectory; using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using AsbCloudInfrastructure.Services.Parser; using AsbCloudInfrastructure.Services.ExcelServices.Templates.TrajectoryTemplates;
namespace AsbCloudInfrastructure.Services.Trajectory.Parser; namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
public class TrajectoryPlanParser : ParserExcelService<TrajectoryGeoPlanDto> public class TrajectoryPlanParser : TrajectoryParser<TrajectoryGeoPlanDto>
{ {
protected override string SheetName => "Плановая траектория"; protected override ITemplateParameters TemplateParameters => new TrajectoryPlanTemplate();
protected override int HeaderRowsCount => 2;
protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx";
protected override IDictionary<string, Cell> Cells => new Dictionary<string, Cell>
{
{ nameof(TrajectoryGeoPlanDto.WellboreDepth), new Cell(1, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.ZenithAngle), new Cell(2, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.AzimuthGeo), new Cell(3, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.AzimuthMagnetic), new Cell(4, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.VerticalDepth), new Cell(5, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.Radius), new Cell(6, typeof(double)) },
{ nameof(TrajectoryGeoPlanDto.Comment), new Cell(7, typeof(string)) }
};
} }

View File

@ -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<WellSectionTypeDto> wellSectionTypeRepository;
private IWellOperationCategoryRepository wellOperationCategoryRepository;
private IWellOperationRepository wellOperationRepository;
/// <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)>()
{
{ (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<WellSectionTypeDto> wellSectionTypeRepository,
IWellOperationCategoryRepository wellOperationCategoryRepository,
IWellOperationRepository wellOperationRepository)
{
this.wellSectionTypeRepository = wellSectionTypeRepository;
this.wellOperationCategoryRepository = wellOperationCategoryRepository;
this.wellOperationRepository = wellOperationRepository;
}
public async Task<List<Dictionary<int, WellOperationDataDto>>> GetAsync(IEnumerable<int> 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<Dictionary<int, WellOperationDataDto>>();
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<int, string> sectionTypes,
Dictionary<int, string> 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;
}
}
}

View File

@ -4,15 +4,23 @@ using Xunit;
namespace AsbCloudWebApi.IntegrationTests; namespace AsbCloudWebApi.IntegrationTests;
public abstract class BaseIntegrationTest : IClassFixture<WebAppFactoryFixture> public abstract class BaseIntegrationTest : IClassFixture<WebAppFactoryFixture>,
IDisposable
{ {
protected readonly IServiceScope scope; protected readonly IServiceScope scope;
protected readonly IAsbCloudDbContext dbContext; protected readonly AsbCloudDbContext dbContext;
protected BaseIntegrationTest(WebAppFactoryFixture factory) protected BaseIntegrationTest(WebAppFactoryFixture factory)
{ {
scope = factory.Services.CreateScope(); scope = factory.Services.CreateScope();
dbContext = scope.ServiceProvider.GetRequiredService<IAsbCloudDbContext>();
dbContext = scope.ServiceProvider.GetRequiredService<AsbCloudDbContext>();
}
public void Dispose()
{
scope.Dispose();
dbContext.Dispose();
} }
} }

View File

@ -35,5 +35,5 @@ public interface IProcessMapPlanDrillingClient
[Multipart] [Multipart]
[Post(BaseRoute + "/parse")] [Post(BaseRoute + "/parse")]
Task<IApiResponse<ParserResultDto<ProcessMapPlanDrillingDto>>> Parse(int idWell, [AliasAs("files")] IEnumerable<StreamPart> streams); Task<IApiResponse<ParserResultDto<ProcessMapPlanDrillingDto>>> Parse(int idWell, [AliasAs("file")] StreamPart stream);
} }

View File

@ -1,6 +1,5 @@
using System.Net; using System.Net;
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudDb.Model;
using AsbCloudWebApi.IntegrationTests.Clients; using AsbCloudWebApi.IntegrationTests.Clients;
using Mapster; using Mapster;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -27,8 +26,6 @@ public class AdminDepositControllerTest : BaseIntegrationTest
: base(factory) : base(factory)
{ {
client = factory.GetAuthorizedHttpClient<IAdminDepositClient>(string.Empty); client = factory.GetAuthorizedHttpClient<IAdminDepositClient>(string.Empty);
dbContext.CleanupDbSet<Deposit>();
} }
[Fact] [Fact]
@ -130,11 +127,8 @@ public class AdminDepositControllerTest : BaseIntegrationTest
[Fact] [Fact]
public async Task GetOrDefault_returns_NoContent_for_IdDeposit() public async Task GetOrDefault_returns_NoContent_for_IdDeposit()
{ {
//arrange
const int idInvalid = 0;
//act //act
var responce = await client.GetOrDefaultAsync(idInvalid); var responce = await client.GetOrDefaultAsync(0);
//assert //assert
Assert.Equal(HttpStatusCode.NoContent, responce.StatusCode); Assert.Equal(HttpStatusCode.NoContent, responce.StatusCode);
@ -143,9 +137,6 @@ public class AdminDepositControllerTest : BaseIntegrationTest
[Fact] [Fact]
public async Task GetAll_returns_success() public async Task GetAll_returns_success()
{ {
//arrange
await client.InsertAsync(dto);
//act //act
var response = await client.GetAllAsync(); var response = await client.GetAllAsync();
@ -155,10 +146,6 @@ public class AdminDepositControllerTest : BaseIntegrationTest
var expectedCount = await dbContext.Deposits.CountAsync(); var expectedCount = await dbContext.Deposits.CountAsync();
Assert.Equal(expectedCount, response.Content.Count()); Assert.Equal(expectedCount, response.Content.Count());
var deposit = response.Content.First();
var excludeProps = new[] { nameof(DepositBaseDto.Id) };
MatchHelper.Match(dto, deposit, excludeProps);
} }
[Fact] [Fact]
@ -182,11 +169,8 @@ public class AdminDepositControllerTest : BaseIntegrationTest
[Fact] [Fact]
public async Task Delete_returns_NoContent_IdDeposit() public async Task Delete_returns_NoContent_IdDeposit()
{ {
//arrange
const int idInvalid = 0;
//act //act
var response = await client.DeleteAsync(idInvalid); var response = await client.DeleteAsync(0);
//assert //assert
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);

View File

@ -15,6 +15,8 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers.ProcessMapPlan;
public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
{ {
private const int IdWell = 1;
private readonly ProcessMapPlanDrillingDto dto = new (){ private readonly ProcessMapPlanDrillingDto dto = new (){
Id = 0, Id = 0,
Creation = new(), Creation = new(),
@ -22,14 +24,13 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
IdState = 0, IdState = 0,
IdPrevious = null, IdPrevious = null,
IdWell = 1, IdWell = IdWell,
Section = "Кондуктор", Section = "Кондуктор",
IdWellSectionType = 3, IdWellSectionType = 3,
DepthStart = 0.5, DepthStart = 0.5,
DepthEnd = 1.5, DepthEnd = 1.5,
IdMode = 1, IdMode = 1,
Mode = "Ротор",
AxialLoadPlan = 2.718281, AxialLoadPlan = 2.718281,
AxialLoadLimitMax = 3.1415926, AxialLoadLimitMax = 3.1415926,
DeltaPressurePlan = 4, DeltaPressurePlan = 4,
@ -55,7 +56,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
IdState = AsbCloudDb.Model.ChangeLogAbstract.IdStateActual, IdState = AsbCloudDb.Model.ChangeLogAbstract.IdStateActual,
IdPrevious = null, IdPrevious = null,
IdWell = 1, IdWell = IdWell,
IdWellSectionType = 1, IdWellSectionType = 1,
DepthStart = 0.5, DepthStart = 0.5,
DepthEnd = 1.5, DepthEnd = 1.5,
@ -115,7 +116,6 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
nameof(ProcessMapPlanDrillingDto.IdState), nameof(ProcessMapPlanDrillingDto.IdState),
nameof(ProcessMapPlanDrillingDto.Author), nameof(ProcessMapPlanDrillingDto.Author),
nameof(ProcessMapPlanDrillingDto.Creation), nameof(ProcessMapPlanDrillingDto.Creation),
nameof(ProcessMapPlanDrillingDto.Mode),
nameof(ProcessMapPlanDrillingDto.Section) nameof(ProcessMapPlanDrillingDto.Section)
}; };
MatchHelper.Match(expected, actual, excludeProps); MatchHelper.Match(expected, actual, excludeProps);
@ -148,21 +148,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
//assert //assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
} }
[Fact]
public async Task InsertRange_returns_BadRequest_for_IdWell()
{
//arrange
var badDto = dto.Adapt<ProcessMapPlanDrillingDto>();
badDto.IdWell = int.MaxValue;
//act
var response = await client.InsertRange(dto.IdWell, new[] { badDto });
//assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact] [Fact]
public async Task ClearAndInsertRange_returns_success() public async Task ClearAndInsertRange_returns_success()
{ {
@ -214,6 +200,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
entry.State = EntityState.Detached; entry.State = EntityState.Detached;
var dtoUpdate = dto.Adapt<ProcessMapPlanDrillingDto>(); var dtoUpdate = dto.Adapt<ProcessMapPlanDrillingDto>();
dtoUpdate.IdWell = 0;
dtoUpdate.Id = entry.Entity.Id; dtoUpdate.Id = entry.Entity.Id;
dtoUpdate.Comment = "nebuchadnezzar"; dtoUpdate.Comment = "nebuchadnezzar";
dtoUpdate.DeltaPressureLimitMax++; dtoUpdate.DeltaPressureLimitMax++;
@ -275,6 +262,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
var expected = dtoUpdate.Adapt<ProcessMapPlanDrilling>(); var expected = dtoUpdate.Adapt<ProcessMapPlanDrilling>();
var excludeProps = new[] { var excludeProps = new[] {
nameof(ProcessMapPlanDrilling.Id), nameof(ProcessMapPlanDrilling.Id),
nameof(ProcessMapPlanDrilling.IdWell),
nameof(ProcessMapPlanDrilling.Author), nameof(ProcessMapPlanDrilling.Author),
nameof(ProcessMapPlanDrilling.IdAuthor), nameof(ProcessMapPlanDrilling.IdAuthor),
nameof(ProcessMapPlanDrilling.Editor), nameof(ProcessMapPlanDrilling.Editor),
@ -346,7 +334,6 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
Assert.InRange(actual.Obsolete.Value, startTime, doneTime); Assert.InRange(actual.Obsolete.Value, startTime, doneTime);
} }
[Fact] [Fact]
public async Task GetDatesChange_returns_success() public async Task GetDatesChange_returns_success()
{ {
@ -358,7 +345,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
dbset.Add(entity); dbset.Add(entity);
dbset.Add(entity2); dbset.Add(entity2);
dbContext.SaveChanges(); dbContext.SaveChanges();
var timezoneHours = Data.Defaults.Wells[0].Timezone.Hours; var timezoneHours = Defaults.Timezone.Hours;
var offset = TimeSpan.FromHours(timezoneHours); var offset = TimeSpan.FromHours(timezoneHours);
var dates = new[] { entity.Creation, entity2.Creation } var dates = new[] { entity.Creation, entity2.Creation }
.Select(d => d.ToOffset(offset)) .Select(d => d.ToOffset(offset))
@ -537,7 +524,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
dbContext.SaveChanges(); dbContext.SaveChanges();
var timezoneHours = Data.Defaults.Wells[0].Timezone.Hours; var timezoneHours = Defaults.Timezone.Hours;
var offset = TimeSpan.FromHours(timezoneHours); var offset = TimeSpan.FromHours(timezoneHours);
var updateFrom = entity.Creation.ToOffset(offset).AddHours(0.5); var updateFrom = entity.Creation.ToOffset(offset).AddHours(0.5);
@ -572,9 +559,10 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
const string fileName = "ProcessMapPlanDrillingValid.xlsx"; const string fileName = "ProcessMapPlanDrillingValid.xlsx";
var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName); var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName);
//act
var streamPart = new StreamPart(stream, fileName, "application/octet-stream"); var streamPart = new StreamPart(stream, fileName, "application/octet-stream");
var response = await client.Parse(Defaults.Wells[0].Id, new[] { streamPart });
//act
var response = await client.Parse(IdWell, streamPart);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -601,9 +589,10 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
const string fileName = "ProcessMapPlanDrillingInvalid.xlsx"; const string fileName = "ProcessMapPlanDrillingInvalid.xlsx";
var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName); var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName);
//act
var streamPart = new StreamPart(stream, fileName, "application/octet-stream"); var streamPart = new StreamPart(stream, fileName, "application/octet-stream");
var response = await client.Parse(Defaults.Wells[0].Id, new[] { streamPart });
//act
var response = await client.Parse(IdWell, streamPart);
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -619,4 +608,4 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
Assert.False(row.IsValid); Assert.False(row.IsValid);
Assert.Equal(2, row.Warnings.Count()); Assert.Equal(2, row.Warnings.Count());
} }
} }

View File

@ -2,314 +2,255 @@
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudDb.Model.ProcessMaps; using AsbCloudDb.Model.ProcessMaps;
using AsbCloudWebApi.IntegrationTests.Clients; using AsbCloudWebApi.IntegrationTests.Clients;
using Org.BouncyCastle.Asn1.Ocsp;
using System.Net; using System.Net;
using System.Net.Http.Headers; using AsbCloudWebApi.IntegrationTests.Data;
using Xunit; using Xunit;
namespace AsbCloudWebApi.IntegrationTests.Controllers.ProcessMapPlan; namespace AsbCloudWebApi.IntegrationTests.Controllers.ProcessMapPlan;
public class ProcessMapReportDrillingControllerTest : BaseIntegrationTest public class ProcessMapReportDrillingControllerTest : BaseIntegrationTest
{ {
private IProcessMapReportDrilling client;
private readonly WellSectionType wellSection = new WellSectionType() public ProcessMapReportDrillingControllerTest(WebAppFactoryFixture factory)
{ : base(factory)
Id = 1, {
Caption = "Секция 1", dbContext.CleanupDbSet<DataSaubStat>();
Order = 1 dbContext.CleanupDbSet<ProcessMapPlanDrilling>();
}; client = factory.GetAuthorizedHttpClient<IProcessMapReportDrilling>(string.Empty);
}
private readonly WellOperationCategory wellOperationCategory = new WellOperationCategory() [Fact]
{ public async Task Get_rtk_report_by_default_request_returns_success()
Id = 1, {
IdParent = null, //arrange
KeyValueName = "Name", var well = dbContext.Wells.First();
Name = "Name"
};
private readonly WellOperation wellOperation = new WellOperation() var processMapPlanDrilling = CreateProcessMapPlanDrilling(well.Id);
{ dbContext.ProcessMapPlanDrilling.Add(processMapPlanDrilling);
CategoryInfo = "CategoryInfo",
Id = 10,
Comment = "Comment",
DateStart = DateTimeOffset.UtcNow,
DepthEnd = 1,
DepthStart = 0.6,
DurationHours = 10,
IdCategory = 1,
IdPlan = null,
IdType = 1,
IdUser = 1,
IdWell = 1,
IdWellSectionType = 1,
LastUpdateDate = DateTimeOffset.UtcNow
};
private readonly List<DataSaubStat> dataSaubStats = new List<DataSaubStat>() var wellOperation = CreateWellOperation(well.Id);
{ dbContext.WellOperations.Add(wellOperation);
new DataSaubStat() var dataSaubStats = CreateDataSaubStats(well.IdTelemetry!.Value, wellOperation.IdCategory);
{ dbContext.DataSaubStat.AddRange(dataSaubStats);
AxialLoad = 0,
AxialLoadLimitMax = 10,
AxialLoadSp = 8,
BlockSpeedSp = 50.0,
DateEnd = DateTimeOffset.UtcNow.AddMinutes(40),
DateStart = DateTimeOffset.UtcNow.AddMinutes(30),
DepthEnd = 85.99299621582031,
DepthStart = 85.9260025024414,
EnabledSubsystems = 0,
Flow = 10,
HasOscillation = true,
Id = 1,
IdCategory = 1,
IdFeedRegulator = 0,
IdTelemetry = 1,
Pressure = 24,
PressureIdle = 0,
PressureSp = 40,
RotorSpeed = 11.3,
RotorTorque = 1,
RotorTorqueLimitMax = 26.5,
RotorTorqueSp = 5,
Speed = 80.3924560546875
},
new DataSaubStat()
{
AxialLoad = 2,
AxialLoadLimitMax = 10.0,
AxialLoadSp = 8,
BlockSpeedSp = 20,
DateEnd = DateTimeOffset.UtcNow.AddMinutes(30),
DateStart = DateTimeOffset.UtcNow.AddMinutes(20),
DepthEnd = 86.28099822998047,
DepthStart = 86.21900177001953,
EnabledSubsystems = 1,
Flow = 20,
HasOscillation = true,
Id = 2,
IdCategory = 1,
IdFeedRegulator = 1,
IdTelemetry = 1,
Pressure = 30,
PressureIdle = 20,
PressureSp = 40,
RotorSpeed = 11.251153300212916,
RotorTorque = 7,
RotorTorqueLimitMax = 26.5,
RotorTorqueSp = 9,
Speed = 74.395751953125
},
new DataSaubStat()
{
AxialLoad = 4,
AxialLoadLimitMax = 15.0,
AxialLoadSp = 8,
BlockSpeedSp = 110.0,
DateEnd = DateTimeOffset.UtcNow.AddMinutes(20),
DateStart = DateTimeOffset.UtcNow.AddMinutes(10),
DepthEnd = 106.7490005493164,
DepthStart = 106.47899627685547,
EnabledSubsystems = 1,
Flow = 30,
HasOscillation = true,
Id = 3,
IdCategory = 1,
IdFeedRegulator = 1,
IdTelemetry = 1,
Pressure = 36,
PressureIdle = 23.0,
PressureSp = 63.0,
RotorSpeed = 11.334207942999628,
RotorTorque = 14,
RotorTorqueLimitMax = 15.0,
RotorTorqueSp = 13,
Speed = 108.001708984375
}
};
await dbContext.SaveChangesAsync();
//act
var request = new DataSaubStatRequest();
var response = await client.GetReportAsync(well.Id, request, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.Equal(dataSaubStats.Count() - 1, response.Content.Count());
}
private readonly ProcessMapPlanDrilling entity = new() [Fact]
{ public async Task Get_rtk_report_by_parametrize_request_returns_success()
Id = 0, {
IdAuthor = 1, //arrange
IdEditor = null, var well = dbContext.Wells.First();
Creation = DateTimeOffset.UtcNow,
Obsolete = null,
IdState = AsbCloudDb.Model.ChangeLogAbstract.IdStateActual,
IdPrevious = null,
IdWell = 1, var processMapPlanDrilling = CreateProcessMapPlanDrilling(well.Id);
IdWellSectionType = 1, dbContext.ProcessMapPlanDrilling.Add(processMapPlanDrilling);
DepthStart = 0.5,
DepthEnd = 1.5,
IdMode = 1, var wellOperation = CreateWellOperation(well.Id);
AxialLoadPlan = 2.718281, dbContext.WellOperations.Add(wellOperation);
AxialLoadLimitMax = 3.1415926,
DeltaPressurePlan = 4,
DeltaPressureLimitMax = 5,
TopDriveTorquePlan = 6,
TopDriveTorqueLimitMax = 7,
TopDriveSpeedPlan = 8,
TopDriveSpeedLimitMax = 9,
FlowPlan = 10,
FlowLimitMax = 11,
RopPlan = 12,
UsageSaub = 13,
UsageSpin = 14,
Comment = "это тестовая запись",
};
private IProcessMapReportDrilling client; var dataSaubStats = CreateDataSaubStats(well.IdTelemetry!.Value, wellOperation.IdCategory);
dbContext.DataSaubStat.AddRange(dataSaubStats);
public ProcessMapReportDrillingControllerTest(WebAppFactoryFixture factory) : base(factory) await dbContext.SaveChangesAsync();
{
client = factory.GetAuthorizedHttpClient<IProcessMapReportDrilling>(string.Empty); var request = new DataSaubStatRequest
} {
DeltaAxialLoad = 5,
DeltaPressure = 15,
DeltaRotorTorque = 10
};
//act
var response = await client.GetReportAsync(well.Id, request, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.Equal(1, response.Content.Count());
}
[Fact]
public async Task Get_rtk_report_returns_BadRequest()
{
//act
var request = new DataSaubStatRequest()
{
DeltaAxialLoad = 15,
DeltaPressure = 25,
DeltaRotorTorque = 20
};
var response = await client.GetReportAsync(0, request, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task Export_rtk_report_returns_success()
{
//arrange
var well = dbContext.Wells.First();
var processMapPlanDrilling = CreateProcessMapPlanDrilling(well.Id);
dbContext.ProcessMapPlanDrilling.Add(processMapPlanDrilling);
[Fact] var wellOperation = CreateWellOperation(well.Id);
public async Task Get_rtk_report_by_default_request_returns_success() dbContext.WellOperations.Add(wellOperation);
{
//arrange
dbContext.CleanupDbSet<ProcessMapPlanDrilling>();
var dbset = dbContext.Set<ProcessMapPlanDrilling>();
dbset.Add(entity);
entity.Id = 1;
dbset.Add(entity);
entity.Id = 2;
dbset.Add(entity);
var dbSetWellOperationCategory = dbContext.Set<WellOperationCategory>(); var dataSaubStats = CreateDataSaubStats(well.IdTelemetry!.Value, wellOperation.IdCategory);
dbSetWellOperationCategory.Add(wellOperationCategory); dbContext.DataSaubStat.AddRange(dataSaubStats);
var dbSetWellOperation = dbContext.Set<WellOperation>(); await dbContext.SaveChangesAsync();
dbSetWellOperation.Add(wellOperation);
//act
var request = new DataSaubStatRequest();
var response = await client.ExportReportAsync(1, request, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("application/octet-stream", response.ContentHeaders?.ContentType?.MediaType);
Assert.True(response.ContentHeaders?.ContentLength > 0);
}
var dbSetDataSaubStat = dbContext.Set<DataSaubStat>(); private static WellOperation CreateWellOperation(int idWell) =>
dbSetDataSaubStat.AddRange(dataSaubStats); new()
{
CategoryInfo = "CategoryInfo",
Comment = "Comment",
DateStart = DateTimeOffset.UtcNow,
DepthEnd = 1,
DepthStart = 0.6,
DurationHours = 10,
IdCategory = WellOperationCategory.IdSlide,
IdPlan = null,
IdType = WellOperation.IdOperationTypeFact,
IdUser = 1,
IdWell = idWell,
IdWellSectionType = 1,
LastUpdateDate = DateTimeOffset.UtcNow
};
dbContext.SaveChanges(); private static ProcessMapPlanDrilling CreateProcessMapPlanDrilling(int idWell) =>
new()
{
IdAuthor = 1,
IdEditor = null,
Creation = DateTimeOffset.UtcNow,
Obsolete = null,
IdState = ChangeLogAbstract.IdStateActual,
IdPrevious = null,
//act IdWell = idWell,
var request = new DataSaubStatRequest(); IdWellSectionType = 1,
var response = await client.GetReportAsync(1, request, CancellationToken.None); DepthStart = 0.5,
DepthEnd = 1.5,
//assert IdMode = 1,
Assert.Equal(HttpStatusCode.OK, response.StatusCode); AxialLoadPlan = 2.718281,
Assert.NotNull(response.Content); AxialLoadLimitMax = 3.1415926,
Assert.Equal(dataSaubStats.Count() - 1, response.Content.Count()); DeltaPressurePlan = 4,
} DeltaPressureLimitMax = 5,
TopDriveTorquePlan = 6,
TopDriveTorqueLimitMax = 7,
TopDriveSpeedPlan = 8,
TopDriveSpeedLimitMax = 9,
FlowPlan = 10,
FlowLimitMax = 11,
RopPlan = 12,
UsageSaub = 13,
UsageSpin = 14,
Comment = "это тестовая запись",
};
[Fact] private static IEnumerable<DataSaubStat> CreateDataSaubStats(int idTelemetry,
public async Task Get_rtk_report_by_parametrize_request_returns_success() int idCategory) =>
{ new[]
//arrange {
dbContext.CleanupDbSet<ProcessMapPlanDrilling>(); new DataSaubStat
var dbset = dbContext.Set<ProcessMapPlanDrilling>(); {
dbset.Add(entity); AxialLoad = 0,
entity.Id = 1; AxialLoadLimitMax = 10,
dbset.Add(entity); AxialLoadSp = 8,
entity.Id = 2; BlockSpeedSp = 50.0,
dbset.Add(entity); DateEnd = DateTimeOffset.UtcNow.AddMinutes(40),
DateStart = DateTimeOffset.UtcNow.AddMinutes(30),
var dbSetWellOperationCategory = dbContext.Set<WellOperationCategory>(); DepthEnd = 85.99299621582031,
dbSetWellOperationCategory.Add(wellOperationCategory); DepthStart = 85.9260025024414,
EnabledSubsystems = 0,
var dbSetWellOperation = dbContext.Set<WellOperation>(); Flow = 10,
dbSetWellOperation.Add(wellOperation); IdCategory = idCategory,
HasOscillation = true,
var dbSetDataSaubStat = dbContext.Set<DataSaubStat>(); IdFeedRegulator = 0,
dbSetDataSaubStat.AddRange(dataSaubStats); IdTelemetry = idTelemetry,
Pressure = 24,
dbContext.SaveChanges(); PressureIdle = 0,
PressureSp = 40,
//act RotorSpeed = 11.3,
var request = new DataSaubStatRequest() RotorTorque = 1,
{ RotorTorqueLimitMax = 26.5,
DeltaAxialLoad = 5, RotorTorqueSp = 5,
DeltaPressure = 15, Speed = 80.3924560546875
DeltaRotorTorque = 10 },
}; new DataSaubStat
var response = await client.GetReportAsync(1, request, CancellationToken.None); {
AxialLoad = 2,
//assert AxialLoadLimitMax = 10.0,
Assert.Equal(HttpStatusCode.OK, response.StatusCode); AxialLoadSp = 8,
Assert.NotNull(response.Content); BlockSpeedSp = 20,
Assert.Equal(1, response.Content.Count()); DateEnd = DateTimeOffset.UtcNow.AddMinutes(30),
} DateStart = DateTimeOffset.UtcNow.AddMinutes(20),
DepthEnd = 86.28099822998047,
[Fact] DepthStart = 86.21900177001953,
public async Task Get_rtk_report_returns_BadRequest() EnabledSubsystems = 1,
{ Flow = 20,
//arrange HasOscillation = true,
dbContext.CleanupDbSet<ProcessMapPlanDrilling>(); IdCategory = idCategory,
var dbset = dbContext.Set<ProcessMapPlanDrilling>(); IdFeedRegulator = 1,
dbset.Add(entity); IdTelemetry = idTelemetry,
entity.Id = 1; Pressure = 30,
dbset.Add(entity); PressureIdle = 20,
entity.Id = 2; PressureSp = 40,
dbset.Add(entity); RotorSpeed = 11.251153300212916,
RotorTorque = 7,
var dbSetWellOperationCategory = dbContext.Set<WellOperationCategory>(); RotorTorqueLimitMax = 26.5,
dbSetWellOperationCategory.Add(wellOperationCategory); RotorTorqueSp = 9,
Speed = 74.395751953125
var dbSetWellOperation = dbContext.Set<WellOperation>(); },
dbSetWellOperation.Add(wellOperation); new DataSaubStat
{
var dbSetDataSaubStat = dbContext.Set<DataSaubStat>(); AxialLoad = 4,
dbSetDataSaubStat.AddRange(dataSaubStats); AxialLoadLimitMax = 15.0,
AxialLoadSp = 8,
dbContext.SaveChanges(); BlockSpeedSp = 110.0,
DateEnd = DateTimeOffset.UtcNow.AddMinutes(20),
//act DateStart = DateTimeOffset.UtcNow.AddMinutes(10),
var request = new DataSaubStatRequest() DepthEnd = 106.7490005493164,
{ DepthStart = 106.47899627685547,
DeltaAxialLoad = 15, EnabledSubsystems = 1,
DeltaPressure = 25, Flow = 30,
DeltaRotorTorque = 20 HasOscillation = true,
}; IdFeedRegulator = 1,
var response = await client.GetReportAsync(1, request, CancellationToken.None); IdTelemetry = idTelemetry,
IdCategory = idCategory,
//assert Pressure = 36,
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); PressureIdle = 23.0,
} PressureSp = 63.0,
RotorSpeed = 11.334207942999628,
[Fact] RotorTorque = 14,
public async Task Export_rtk_report_returns_success() RotorTorqueLimitMax = 15.0,
{ RotorTorqueSp = 13,
//clear Speed = 108.001708984375
dbContext.CleanupDbSet<ProcessMapPlanDrilling>(); }
};
//arrange }
var dbset = dbContext.Set<ProcessMapPlanDrilling>();
dbset.Add(entity);
entity.Id = 1;
dbset.Add(entity);
entity.Id = 2;
dbset.Add(entity);
var dbSetWellOperationCategory = dbContext.Set<WellOperationCategory>();
dbSetWellOperationCategory.Add(wellOperationCategory);
var dbSetWellOperation = dbContext.Set<WellOperation>();
dbSetWellOperation.Add(wellOperation);
var dbSetDataSaubStat = dbContext.Set<DataSaubStat>();
dbSetDataSaubStat.AddRange(dataSaubStats);
dbContext.SaveChanges();
//act
var request = new DataSaubStatRequest();
var response = await client.ExportReportAsync(1, request, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("application/octet-stream", response.ContentHeaders?.ContentType?.MediaType);
Assert.True(response.ContentHeaders?.ContentLength > 0);
}
}

View File

@ -1,228 +1,228 @@
using AsbCloudApp.Data; // using AsbCloudApp.Data;
using AsbCloudDb.Model; // using AsbCloudDb.Model;
using AsbCloudInfrastructure; // using AsbCloudInfrastructure;
using AsbCloudWebApi.IntegrationTests.Clients; // using AsbCloudWebApi.IntegrationTests.Clients;
using Mapster; // using Mapster;
using Microsoft.EntityFrameworkCore; // using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking; // using Microsoft.EntityFrameworkCore.ChangeTracking;
using System.Net; // using System.Net;
using Xunit; // using Xunit;
//
namespace AsbCloudWebApi.IntegrationTests.Controllers // namespace AsbCloudWebApi.IntegrationTests.Controllers
{ // {
public abstract class CrudWellRelatedClient<TDto, TEntity> : BaseIntegrationTest // public abstract class CrudWellRelatedClient<TDto, TEntity> : BaseIntegrationTest
where TDto : AsbCloudApp.Data.IId, AsbCloudApp.Data.IWellRelated // where TDto : AsbCloudApp.Data.IId, AsbCloudApp.Data.IWellRelated
where TEntity : class, AsbCloudDb.Model.IId, AsbCloudDb.Model.IWellRelated // where TEntity : class, AsbCloudDb.Model.IId, AsbCloudDb.Model.IWellRelated
{ // {
public abstract IEnumerable<TDto> ValidDtos { get; } // public abstract IEnumerable<TDto> ValidDtos { get; }
public abstract IEnumerable<TDto> InvalidDtos { get; } // public abstract IEnumerable<TDto> InvalidDtos { get; }
public abstract IEnumerable<TDto> ForbiddenDtos { get; } // public abstract IEnumerable<TDto> ForbiddenDtos { get; }
protected List<string> ExcludeProps { get; set; } = new() { "Id" }; // protected List<string> ExcludeProps { get; set; } = new() { "Id" };
//
protected ICrudWellRelatedClient<TDto> client; // protected ICrudWellRelatedClient<TDto> client;
//
public CrudWellRelatedClient(WebAppFactoryFixture factory, string uriSuffix) // public CrudWellRelatedClient(WebAppFactoryFixture factory, string uriSuffix)
: base(factory) // : base(factory)
{ // {
client = factory.GetAuthorizedHttpClient<ICrudWellRelatedClient<TDto>>(uriSuffix); // client = factory.GetAuthorizedHttpClient<ICrudWellRelatedClient<TDto>>(uriSuffix);
} // }
//
protected async Task<DbSet<TEntity>> GetCleanDbSet() // protected async Task<DbSet<TEntity>> GetCleanDbSet()
{ // {
var dbset = dbContext.Set<TEntity>(); // var dbset = dbContext.Set<TEntity>();
dbset.RemoveRange(dbset); // dbset.RemoveRange(dbset);
await dbContext.SaveChangesAsync(CancellationToken.None); // await dbContext.SaveChangesAsync(CancellationToken.None);
return dbset; // return dbset;
} // }
//
[Fact] // [Fact]
public async Task Insert_returns_success_for_validDtos() // public async Task Insert_returns_success_for_validDtos()
{ // {
foreach (var validDto in ValidDtos) // foreach (var validDto in ValidDtos)
await Insert_returns_success(validDto); // await Insert_returns_success(validDto);
} // }
//
private async Task Insert_returns_success(TDto validDto) // private async Task Insert_returns_success(TDto validDto)
{ // {
var dbset = await GetCleanDbSet(); // var dbset = await GetCleanDbSet();
//
//act // //act
var response = await client.InsertAsync(validDto); // var response = await client.InsertAsync(validDto);
//
//assert // //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.True(response.Content > 0); // Assert.True(response.Content > 0);
//
var entity = dbset.First(); // var entity = dbset.First();
var fromDbDto = Convert(entity); // var fromDbDto = Convert(entity);
MatchHelper.Match(validDto, fromDbDto, ExcludeProps); // MatchHelper.Match(validDto, fromDbDto, ExcludeProps);
} // }
//
[Fact] // [Fact]
public async Task Insert_returns_badRequest_for_invalidDtos() // public async Task Insert_returns_badRequest_for_invalidDtos()
{ // {
foreach (var inValidDto in InvalidDtos) // foreach (var inValidDto in InvalidDtos)
await Insert_returns_badRequest(inValidDto); // await Insert_returns_badRequest(inValidDto);
} // }
//
private async Task Insert_returns_badRequest(TDto invalidDto) // private async Task Insert_returns_badRequest(TDto invalidDto)
{ // {
await GetCleanDbSet(); // await GetCleanDbSet();
//
//act // //act
var response = await client.InsertAsync(invalidDto); // var response = await client.InsertAsync(invalidDto);
//
//assert // //assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); // Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
} // }
//
[Fact] // [Fact]
public async Task Insert_returns_forbidden_for_forbiddenDtos() // public async Task Insert_returns_forbidden_for_forbiddenDtos()
{ // {
foreach (var forbiddenDto in ForbiddenDtos) // foreach (var forbiddenDto in ForbiddenDtos)
await Insert_returns_forbidden(forbiddenDto); // await Insert_returns_forbidden(forbiddenDto);
} // }
//
private async Task Insert_returns_forbidden(TDto forbiddenDto) // private async Task Insert_returns_forbidden(TDto forbiddenDto)
{ // {
await GetCleanDbSet(); // await GetCleanDbSet();
//
//act // //act
var response = await client.InsertAsync(forbiddenDto); // var response = await client.InsertAsync(forbiddenDto);
//
//assert // //assert
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); // Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
} // }
//
[Fact] // [Fact]
public async Task GetAllAsync_returns_data() // public async Task GetAllAsync_returns_data()
{ // {
//arrange // //arrange
var dbset = await GetCleanDbSet(); // var dbset = await GetCleanDbSet();
var entries = new List<(EntityEntry<TEntity>, TDto)>(); // var entries = new List<(EntityEntry<TEntity>, TDto)>();
//
foreach (var dto in ValidDtos) // foreach (var dto in ValidDtos)
{ // {
var entity = Convert(dto); // var entity = Convert(dto);
entity.Id = 0; // entity.Id = 0;
var entry = dbset.Add(entity); // var entry = dbset.Add(entity);
entries.Add((entry, dto)); // entries.Add((entry, dto));
} // }
dbContext.SaveChanges(); // dbContext.SaveChanges();
//
//act // //act
var response = await client.GetAllAsync(); // var response = await client.GetAllAsync();
//
//assert // //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); // Assert.NotNull(response.Content);
Assert.Equal(entries.Count, response.Content.Count()); // Assert.Equal(entries.Count, response.Content.Count());
//
foreach (var item in response.Content) // foreach (var item in response.Content)
{ // {
var entry = entries.First(e => e.Item1.Entity.Id == item.Id); // var entry = entries.First(e => e.Item1.Entity.Id == item.Id);
MatchHelper.Match(entry.Item2, item, ExcludeProps); // MatchHelper.Match(entry.Item2, item, ExcludeProps);
} // }
} // }
//
protected virtual TDto Convert(TEntity entity) // protected virtual TDto Convert(TEntity entity)
{ // {
var dto = entity.Adapt<TDto>(); // var dto = entity.Adapt<TDto>();
return dto; // return dto;
} // }
//
protected virtual TEntity Convert(TDto dto) // protected virtual TEntity Convert(TDto dto)
{ // {
var entity = dto.Adapt<TEntity>(); // var entity = dto.Adapt<TEntity>();
return entity; // return entity;
} // }
} // }
//
public class ScheduleControllerTest : CrudWellRelatedClient<ScheduleDto, Schedule> // public class ScheduleControllerTest : CrudWellRelatedClient<ScheduleDto, Schedule>
{ // {
static Driller driller = Data.Defaults.Drillers.First(); // static Driller driller = Data.Defaults.Drillers.First();
static DrillerDto drillerDto = driller.Adapt<DrillerDto>(); // static DrillerDto drillerDto = driller.Adapt<DrillerDto>();
//
static Well well = Data.Defaults.Wells.First(); // static Well well = Data.Defaults.Wells.First();
//
public override IEnumerable<ScheduleDto> ValidDtos { get; } = new ScheduleDto[] // public override IEnumerable<ScheduleDto> ValidDtos { get; } = new ScheduleDto[]
{ // {
new() { // new() {
Id = 1, // Id = 1,
IdWell = well.Id, // IdWell = well.Id,
Driller = drillerDto, // Driller = drillerDto,
IdDriller = driller.Id, // IdDriller = driller.Id,
DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), // DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified),
DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), // DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified),
ShiftStart = new TimeDto(8, 0, 0), // ShiftStart = new TimeDto(8, 0, 0),
ShiftEnd = new TimeDto(20, 0, 0), // ShiftEnd = new TimeDto(20, 0, 0),
}, // },
new() { // new() {
Id = 1, // Id = 1,
IdWell = well.Id, // IdWell = well.Id,
Driller = drillerDto, // Driller = drillerDto,
IdDriller = driller.Id, // IdDriller = driller.Id,
DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), // DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified),
DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), // DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified),
ShiftStart = new TimeDto(20, 0, 0), // ShiftStart = new TimeDto(20, 0, 0),
ShiftEnd = new TimeDto(8, 0, 0), // ShiftEnd = new TimeDto(8, 0, 0),
} // }
}; // };
//
public override IEnumerable<ScheduleDto> InvalidDtos { get; } = new ScheduleDto[] // public override IEnumerable<ScheduleDto> InvalidDtos { get; } = new ScheduleDto[]
{ // {
new() { // new() {
IdWell = well.Id, // IdWell = well.Id,
Driller = drillerDto, // Driller = drillerDto,
IdDriller = driller.Id, // IdDriller = driller.Id,
DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), // DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified),
DrillEnd = new DateTime(2022, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), // DrillEnd = new DateTime(2022, 1, 2, 0, 0, 0, DateTimeKind.Unspecified),
ShiftStart = new TimeDto(8, 0, 0), // ShiftStart = new TimeDto(8, 0, 0),
ShiftEnd = new TimeDto(20, 0, 0), // ShiftEnd = new TimeDto(20, 0, 0),
}, // },
new() { // new() {
IdWell = well.Id, // IdWell = well.Id,
Driller = drillerDto, // Driller = drillerDto,
IdDriller = int.MaxValue - 1, // IdDriller = int.MaxValue - 1,
DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), // DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified),
DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), // DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified),
ShiftStart = new TimeDto(8, 0, 0), // ShiftStart = new TimeDto(8, 0, 0),
ShiftEnd = new TimeDto(20, 0, 0), // ShiftEnd = new TimeDto(20, 0, 0),
} // }
}; // };
//
public override IEnumerable<ScheduleDto> ForbiddenDtos { get; } = new ScheduleDto[] { // public override IEnumerable<ScheduleDto> ForbiddenDtos { get; } = new ScheduleDto[] {
new() { // new() {
IdWell = int.MaxValue - 1, // IdWell = int.MaxValue - 1,
Driller = drillerDto, // Driller = drillerDto,
IdDriller = driller.Id, // IdDriller = driller.Id,
DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), // DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified),
DrillEnd = new DateTime(2022, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), // DrillEnd = new DateTime(2022, 1, 2, 0, 0, 0, DateTimeKind.Unspecified),
ShiftStart = new TimeDto(8, 0, 0), // ShiftStart = new TimeDto(8, 0, 0),
ShiftEnd = new TimeDto(20, 0, 0), // ShiftEnd = new TimeDto(20, 0, 0),
} // }
}; // };
//
public ScheduleControllerTest(WebAppFactoryFixture factory) // public ScheduleControllerTest(WebAppFactoryFixture factory)
: base(factory, "api/schedule") // : base(factory, "api/schedule")
{ // {
ExcludeProps.Add(nameof(ScheduleDto.Driller)); // ExcludeProps.Add(nameof(ScheduleDto.Driller));
} // }
//
protected override ScheduleDto Convert(Schedule entity) // protected override ScheduleDto Convert(Schedule entity)
{ // {
var dto = base.Convert(entity); // var dto = base.Convert(entity);
dto.DrillStart = entity.DrillStart.ToRemoteDateTime(well.Timezone.Hours); // dto.DrillStart = entity.DrillStart.ToRemoteDateTime(well.Timezone.Hours);
dto.DrillEnd = entity.DrillEnd.ToRemoteDateTime(well.Timezone.Hours); // dto.DrillEnd = entity.DrillEnd.ToRemoteDateTime(well.Timezone.Hours);
return dto; // return dto;
} // }
//
protected override Schedule Convert(ScheduleDto dto) // protected override Schedule Convert(ScheduleDto dto)
{ // {
var entity = base.Convert(dto); // var entity = base.Convert(dto);
entity.DrillStart = dto.DrillStart.FromTimeZoneOffsetHours(well.Timezone.Hours); // entity.DrillStart = dto.DrillStart.FromTimeZoneOffsetHours(well.Timezone.Hours);
entity.DrillEnd = dto.DrillEnd.FromTimeZoneOffsetHours(well.Timezone.Hours); // entity.DrillEnd = dto.DrillEnd.FromTimeZoneOffsetHours(well.Timezone.Hours);
return entity; // return entity;
} // }
} // }
} // }

View File

@ -2,77 +2,38 @@ using AsbCloudApp.Data;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudWebApi.IntegrationTests.Clients; using AsbCloudWebApi.IntegrationTests.Clients;
using Microsoft.EntityFrameworkCore;
using Xunit; using Xunit;
namespace AsbCloudWebApi.IntegrationTests.Controllers; namespace AsbCloudWebApi.IntegrationTests.Controllers;
public class SlipsStatControllerTest : BaseIntegrationTest public class SlipsStatControllerTest : BaseIntegrationTest
{ {
private static readonly Schedule schedule = new()
{
Id = 0,
IdDriller = Data.Defaults.Drillers[0].Id,
IdWell = Data.Defaults.Wells[0].Id,
ShiftStart = new TimeOnly(8, 0, 0),
ShiftEnd = new TimeOnly(20, 0, 0),
DrillStart = new DateTimeOffset(new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)),
DrillEnd = new DateTimeOffset(new DateTime(2024, 2, 1, 0, 0, 0, DateTimeKind.Utc))
};
private static readonly DetectedOperation detectedOperation = new()
{
Id = 0,
IdTelemetry = Data.Defaults.Telemetries[0].Id,
IdCategory = WellOperationCategory.IdSlipsTime,
DateStart = new DateTimeOffset(new DateTime(2024, 1, 23, 15, 0, 0, 0, DateTimeKind.Utc)),
DateEnd = new DateTimeOffset(new DateTime(2024, 1, 23, 15, 2, 0, 0, DateTimeKind.Utc)),
ExtraData = new Dictionary<string, object>
{
{ "test", 5 }
}
};
private static readonly WellOperation factWellOperation = new()
{
Id = 0,
IdWell = Data.Defaults.Wells[0].Id,
IdWellSectionType = 1,
IdCategory = WellOperationCategory.IdRotor,
IdType = WellOperation.IdOperationTypeFact,
DepthStart = 0,
DepthEnd = 100,
LastUpdateDate = DateTimeOffset.UtcNow,
DateStart = new DateTimeOffset(new DateTime(2024, 1, 15, 15, 0, 0, DateTimeKind.Utc)),
DurationHours = 1
};
private readonly ISlipsTimeClient client; private readonly ISlipsTimeClient client;
public SlipsStatControllerTest(WebAppFactoryFixture factory) public SlipsStatControllerTest(WebAppFactoryFixture factory)
: base(factory) : base(factory)
{ {
var schedules = dbContext.Set<Schedule>();
var detectedOperations = dbContext.Set<DetectedOperation>();
var wellOperations = dbContext.Set<WellOperation>();
schedules.RemoveRange(schedules);
detectedOperations.RemoveRange(detectedOperations);
wellOperations.RemoveRange(wellOperations);
dbContext.SaveChanges();
schedules.Add(schedule);
detectedOperations.Add(detectedOperation);
wellOperations.Add(factWellOperation);
dbContext.SaveChanges();
client = factory.GetAuthorizedHttpClient<ISlipsTimeClient>(string.Empty); client = factory.GetAuthorizedHttpClient<ISlipsTimeClient>(string.Empty);
dbContext.CleanupDbSet<Schedule>();
dbContext.CleanupDbSet<WellOperation>();
dbContext.CleanupDbSet<DetectedOperation>();
} }
[Fact] [Fact]
public async Task GetAll_returns_success() public async Task GetAll_returns_success()
{ {
//arrange //arrange
var well = dbContext.Wells.First();
var schedule = CreateScheduleAsync(well.Id);
var factWellOperation = CreateFactWellOperation(well.Id);
var detectedOperation = CreateDetectedOperation(well.IdTelemetry!.Value);
dbContext.Schedule.Add(schedule);
dbContext.WellOperations.Add(factWellOperation);
dbContext.DetectedOperations.Add(detectedOperation);
await dbContext.SaveChangesAsync();
var request = new OperationStatRequest var request = new OperationStatRequest
{ {
DateStartUTC = schedule.DrillStart.DateTime, DateStartUTC = schedule.DrillStart.DateTime,
@ -83,7 +44,7 @@ public class SlipsStatControllerTest : BaseIntegrationTest
var dtoExpected = new SlipsStatDto var dtoExpected = new SlipsStatDto
{ {
DrillerName = $"{Data.Defaults.Drillers[0].Surname} {Data.Defaults.Drillers[0].Name} {Data.Defaults.Drillers[0].Patronymic}", DrillerName = $"{schedule.Driller.Surname} {schedule.Driller.Name} {schedule.Driller.Patronymic}",
WellCount = 1, WellCount = 1,
SectionCaption = "Пилотный ствол", SectionCaption = "Пилотный ствол",
SlipsCount = 1, SlipsCount = 1,
@ -101,4 +62,46 @@ public class SlipsStatControllerTest : BaseIntegrationTest
var slipsStat = response.Content.First(); var slipsStat = response.Content.First();
MatchHelper.Match(dtoExpected, slipsStat); MatchHelper.Match(dtoExpected, slipsStat);
} }
private static Schedule CreateScheduleAsync(int idWell) => new()
{
IdWell = idWell,
ShiftStart = new TimeOnly(8, 0, 0),
ShiftEnd = new TimeOnly(20, 0, 0),
DrillStart = new DateTimeOffset(new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)),
DrillEnd = new DateTimeOffset(new DateTime(2024, 2, 1, 0, 0, 0, DateTimeKind.Utc)),
Driller = new Driller
{
Name = "TestName",
Surname = "TestSurname",
Patronymic = "TestPatronymic"
}
};
private static WellOperation CreateFactWellOperation(int idWell) =>
new()
{
IdWell = idWell,
IdWellSectionType = 1,
IdCategory = WellOperationCategory.IdRotor,
IdType = WellOperation.IdOperationTypeFact,
DepthStart = 0,
DepthEnd = 100,
LastUpdateDate = DateTimeOffset.UtcNow,
DateStart = new DateTimeOffset(new DateTime(2024, 1, 15, 15, 0, 0, DateTimeKind.Utc)),
DurationHours = 1
};
private static DetectedOperation CreateDetectedOperation(int idTelemetry) =>
new()
{
IdTelemetry = idTelemetry,
IdCategory = WellOperationCategory.IdSlipsTime,
DateStart = new DateTimeOffset(new DateTime(2024, 1, 23, 15, 0, 0, 0, DateTimeKind.Utc)),
DateEnd = new DateTimeOffset(new DateTime(2024, 1, 23, 15, 2, 0, 0, DateTimeKind.Utc)),
ExtraData = new Dictionary<string, object>
{
{ "test", 5 }
}
};
} }

View File

@ -5,6 +5,8 @@ using System.Net;
using System.Reflection; using System.Reflection;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudWebApi.IntegrationTests.Data; using AsbCloudWebApi.IntegrationTests.Data;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Refit; using Refit;
using Xunit; using Xunit;
@ -12,85 +14,76 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers;
public class WellOperationControllerTest : BaseIntegrationTest public class WellOperationControllerTest : BaseIntegrationTest
{ {
private static int idWell = 1;
private readonly WellOperationDto[] dtos = new WellOperationDto[]
{
new()
{
IdWell = 1,
IdWellSectionType = 1,
WellSectionTypeName = "Пилотный ствол",
IdCategory = 5000,
IdPlan = null,
CategoryName = "Разборка КНБК",
IdParentCategory = 4000,
CategoryInfo = "1",
IdType = 0,
DepthStart = 10.0,
DepthEnd = 20.0,
Day = 0.0,
NptHours = 0.0,
DateStart = new DateTimeOffset(new DateTime(2023, 1, 10), TimeSpan.FromHours(Defaults.Wells[0].Timezone.Hours)),
DurationHours = 1.0,
Comment = "1",
IdUser = 1,
UserName = null,
}
};
private IWellOperationClient client; private IWellOperationClient client;
public WellOperationControllerTest(WebAppFactoryFixture factory) public WellOperationControllerTest(WebAppFactoryFixture factory)
: base(factory) : base(factory)
{ {
client = factory.GetAuthorizedHttpClient<IWellOperationClient>(string.Empty); client = factory.GetAuthorizedHttpClient<IWellOperationClient>(string.Empty);
}
/// <summary> dbContext.CleanupDbSet<WellOperation>();
/// Успешное добавление операций (без предварительной очистки данных) }
/// </summary>
/// <returns></returns>
[Fact]
public async Task InsertRange_returns_success()
{
dbContext.CleanupDbSet<WellOperation>();
//act
var response = await client.InsertRangeAsync(idWell, 1, false, dtos);
//assert /// <summary>
Assert.Equal(HttpStatusCode.OK, response.StatusCode); /// Успешное добавление операций (без предварительной очистки данных)
} /// </summary>
/// <returns></returns>
[Fact]
public async Task InsertRange_returns_success()
{
//arrange
var well = await dbContext.Wells.FirstAsync();
var entity = CreateWellOperation(well.Id);
var dtos = new[] { entity.Adapt<WellOperationDto>() };
/// <summary> //act
/// Успешное добавление операций (с предварительной очисткой данных) var response = await client.InsertRangeAsync(well.Id, 1, false, dtos);
/// </summary>
/// <returns></returns>
[Fact]
public async Task InsertRangeWithDeleteBefore_returns_success()
{
//act
var response = await client.InsertRangeAsync(idWell, 1, true, dtos);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
} }
/// <summary> /// <summary>
/// Успешное обновление операции /// Успешное добавление операций (с предварительной очисткой данных)
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Fact] [Fact]
public async Task UpdateAsync_returns_success() public async Task InsertRangeWithDeleteBefore_returns_success()
{ {
//act //arrange
var dto = dtos.FirstOrDefault()!; var well = await dbContext.Wells.FirstAsync();
var response = await client.UpdateAsync(idWell, 1, dto, CancellationToken.None); var entity = CreateWellOperation(well.Id);
var dtos = new[] { entity.Adapt<WellOperationDto>() };
//act
var response = await client.InsertRangeAsync(well.Id, 1, true, dtos);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
/// <summary>
/// Успешное обновление операции
/// </summary>
/// <returns></returns>
[Fact]
public async Task UpdateAsync_returns_success()
{
//arrange
var well = await dbContext.Wells.FirstAsync();
var entity = CreateWellOperation(well.Id);
dbContext.WellOperations.Add(entity);
await dbContext.SaveChangesAsync();
var dto = entity.Adapt<WellOperationDto>();
//act
var response = await client.UpdateAsync(well.Id, entity.Id, dto, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
/// <summary> /// <summary>
/// Получение плановых операций /// Получение плановых операций
/// </summary> /// </summary>
@ -99,8 +92,14 @@ public class WellOperationControllerTest : BaseIntegrationTest
public async Task GetPageOperationsPlanAsync_returns_success() public async Task GetPageOperationsPlanAsync_returns_success()
{ {
//arrange //arrange
dbContext.CleanupDbSet<WellOperation>(); var well = await dbContext.Wells.FirstAsync();
await client.InsertRangeAsync(idWell, WellOperation.IdOperationTypePlan, false, dtos); var entity = CreateWellOperation(well.Id);
dbContext.WellOperations.Add(entity);
await dbContext.SaveChangesAsync();
var dto = entity.Adapt<WellOperationDto>();
var timezoneOffset = TimeSpan.FromHours(well.Timezone.Hours);
dto.DateStart = dto.DateStart.ToOffset(timezoneOffset);
var request = new WellOperationRequestBase var request = new WellOperationRequestBase
{ {
@ -108,49 +107,59 @@ public class WellOperationControllerTest : BaseIntegrationTest
}; };
//act //act
var response = await client.GetPageOperationsPlanAsync(idWell, request, CancellationToken.None); var response = await client.GetPageOperationsPlanAsync(well.Id, request, CancellationToken.None);
//assert //assert
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
Assert.Single(response.Content.Items); Assert.Single(response.Content.Items);
var dto = dtos[0]; var actualDto = response.Content.Items.First();
var wellOperation = response.Content.Items.First();
var excludeProps = new[] { nameof(WellOperationDto.Id) }; var excludeProps = new[]
MatchHelper.Match(dto, wellOperation, excludeProps); {
nameof(WellOperationDto.LastUpdateDate)
};
MatchHelper.Match(dto, actualDto, excludeProps);
} }
[Fact] [Fact]
public async Task ImportPlanDefaultExcelFileAsync_returns_success() public async Task ImportPlanDefaultExcelFileAsync_returns_success()
{ {
//arrange //arrange
//TODO: вынести в метод расширения. Сделать когда доберёмся до рефакторинга операций по скважине var stream = Assembly.GetExecutingAssembly().GetFileCopyStream("WellOperationsPlan.xlsx");
var resourceName = Assembly.GetExecutingAssembly()
.GetManifestResourceNames()
.FirstOrDefault(n => n.EndsWith("WellOperationsPlan.xlsx"));
if (string.IsNullOrWhiteSpace(resourceName))
throw new ArgumentNullException(nameof(resourceName));
var stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(resourceName);
if (stream is null)
throw new ArgumentNullException(nameof(stream));
var memoryStream = new MemoryStream(); var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream); await stream.CopyToAsync(memoryStream);
memoryStream.Position = 0; memoryStream.Position = 0;
var well = await dbContext.Wells.FirstAsync();
//act //act
var streamPart = new StreamPart(memoryStream, "WellOperations.xlsx", "application/octet-stream"); var streamPart = new StreamPart(memoryStream, "WellOperations.xlsx", "application/octet-stream");
var response = await client.ImportPlanDefaultExcelFileAsync(idWell, new[] { streamPart }, CancellationToken.None); var response = await client.ImportPlanDefaultExcelFileAsync(well.Id, new[] { streamPart }, CancellationToken.None);
//assert //assert
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
Assert.Equal(4, response.Content.Count()); Assert.Equal(4, response.Content.Count());
Assert.True(response.Content.All(w => Math.Abs(w.DateStart.Offset.Hours - Defaults.Wells[0].Timezone.Hours) < 0.1)); Assert.True(response.Content.All(w => Math.Abs(w.DateStart.Offset.Hours - Defaults.Timezone.Hours) < 0.1));
} }
private static WellOperation CreateWellOperation(int idWell) =>
new()
{
IdWell = idWell,
IdWellSectionType = 2,
LastUpdateDate = DateTimeOffset.UtcNow,
IdCategory = 5000,
IdPlan = null,
CategoryInfo = "1",
IdType = 0,
DepthStart = 10.0,
DepthEnd = 20.0,
DateStart = new DateTimeOffset(new DateTime(2023, 1, 10), TimeSpan.FromHours(Defaults.Timezone.Hours)).ToUniversalTime(),
DurationHours = 1.0,
Comment = "1",
IdUser = 1
};
} }

View File

@ -2,110 +2,55 @@
namespace AsbCloudWebApi.IntegrationTests.Data namespace AsbCloudWebApi.IntegrationTests.Data
{ {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2211:Поля, не являющиеся константами, не должны быть видимыми", Justification = "<Ожидание>")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2211:Поля, не являющиеся константами, не должны быть видимыми",
public static class Defaults Justification = "<Ожидание>")]
{ public static class Defaults
public static Driller[] Drillers = new Driller[] {
{ public static SimpleTimezone Timezone => new()
new() {
{ Hours = 1
Id = 1, };
Name = "test1",
Surname = "test1",
Patronymic = "test1"
},
new()
{
Id = 2,
Name = "test2",
Surname = "test2",
Patronymic = "test2"
}
};
public static WellOperation[] WellOperations = new WellOperation[] public static Deposit[] Deposits => new Deposit[]
{ {
new() new()
{ {
Id = 1, Caption = "Deposit1",
IdWell = 1, Latitude = 10,
IdType = 1, Longitude = 20,
DateStart = DateTimeOffset.UtcNow.AddDays(-1), Timezone = Timezone,
CategoryInfo = "1", Clusters = new[]
Comment = "1", {
DepthEnd = 20, new Cluster
DepthStart = 10, {
DurationHours = 1, Caption = "Cluster1",
IdCategory = 5000, Latitude = 10,
IdPlan = null, Longitude = 20,
IdUser = 1, Timezone = Timezone,
IdWellSectionType = 1, Wells = new[]
LastUpdateDate = DateTimeOffset.UtcNow {
} new Well
}; {
IdWellType = 1,
public static Deposit[] Deposits = new Deposit[] { IdState = 1,
new() Caption = "Well1",
{ Latitude = 10,
Id = 1, Longitude = 20,
Caption = "Deposit1", Timezone = Timezone,
Latitude = 10, Telemetry = new Telemetry
Longitude = 20, {
Timezone = GetTimezone() RemoteUid = "555-555-555",
} TimeZone = Timezone
}; },
RelationCompaniesWells = new RelationCompanyWell[]
public static Cluster[] Clusters = new Cluster[] { {
new() new() { IdCompany = 1 },
{ },
Id = 1, }
IdDeposit = Deposits[0].Id, }
Caption = "Cluster1", }
Latitude = 10, }
Longitude = 20, }
Timezone = GetTimezone() };
} }
}; }
public static Telemetry[] Telemetries = new Telemetry[]
{
new()
{
Id = 1,
RemoteUid = "555-555-555",
TimeZone = GetTimezone()
}
};
public static Well[] Wells = new Well[] {
new()
{
Id = 1,
IdCluster = Clusters[0].Id,
IdWellType = 1,
IdState = 1,
Caption = "Well1",
Latitude = 10,
Longitude = 20,
IdTelemetry = Telemetries[0].Id,
Timezone = GetTimezone()
}
};
public static RelationCompanyWell[] RelationsCompanyWell = new RelationCompanyWell[]
{
new() { IdCompany = 1, IdWell = Wells[0].Id },
};
public static RelationUserUserRole[] RelationsUserUserRole = new RelationUserUserRole[]
{
new(){ IdUserRole= 1, IdUser = 1}
};
private static SimpleTimezone GetTimezone() =>
new ()
{
Hours = 1
};
}
}

View File

@ -0,0 +1,15 @@
namespace AsbCloudWebApi.IntegrationTests;
public class DbConnection
{
public string Host { get; set; } = null!;
public int Port { get; set; }
public string Username { get; set; } = null!;
public string Password { get; set; } = null!;
public string GetConnectionString() =>
$"Host={Host};Database={Guid.NewGuid()};Port={Port};Username={Username};Password={Password};Persist Security Info=True;Include Error Detail=True";
}

View File

@ -2,6 +2,7 @@
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using Mapster; using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Xunit; using Xunit;
@ -9,157 +10,128 @@ namespace AsbCloudWebApi.IntegrationTests.Repository;
public class DataSaubStatRepositoryTest : BaseIntegrationTest public class DataSaubStatRepositoryTest : BaseIntegrationTest
{ {
private static readonly TimeSpan timeSpan = TimeSpan.FromHours(1); private readonly IDataSaubStatRepository dataSaubStatRepository;
public DataSaubStatRepositoryTest(WebAppFactoryFixture factory)
: base(factory)
{
dataSaubStatRepository = scope.ServiceProvider.GetRequiredService<IDataSaubStatRepository>();
dbContext.CleanupDbSet<DataSaubStat>();
}
private static readonly DataSaubStatDto[] statDtos = new DataSaubStatDto[2] [Fact]
{ public async Task GetLastDatesAsync_returns_success()
new() {
{ //arrange
IdTelemetry = 1, var telemetry = await dbContext.Telemetries.FirstAsync();
DateEnd = new DateTimeOffset(2024, 1, 1, 20, 25, 0, timeSpan), var timeZoneOffset = TimeSpan.FromHours(telemetry.TimeZone.Hours);
DateStart = new DateTimeOffset(2024, 1, 1, 20, 15, 0, timeSpan), var dataSaubStat = CreateDataSaubStat(telemetry.Id, timeZoneOffset);
AxialLoad = 10.0, dbContext.AddRange(dataSaubStat);
AxialLoadLimitMax = 10.0, await dbContext.SaveChangesAsync();
AxialLoadSp = 10.0,
BlockSpeedSp = 1000,
DepthEnd = 10.0,
DepthStart = 5.0,
EnabledSubsystems = 1,
Flow = 10.0,
HasOscillation = true,
Id = default,
IdCategory = 2,
IdFeedRegulator = 1,
Pressure = 10.0,
PressureIdle = 10.0,
PressureSp = 10.0,
RotorSpeed = 9.0,
RotorTorque = 9.0,
RotorTorqueSp = 9.0,
RotorTorqueLimitMax = 9.0,
Speed = 10.0
},
new()
{
IdTelemetry = 1,
DateEnd = new DateTimeOffset(2024, 2, 2, 20, 25, 0, timeSpan),
DateStart = new DateTimeOffset(2024, 2, 2, 20, 15, 0, timeSpan),
AxialLoad = 10.0,
AxialLoadLimitMax = 10.0,
AxialLoadSp = 10.0,
BlockSpeedSp = 1000,
DepthEnd = 10.0,
DepthStart = 5.0,
EnabledSubsystems = 1,
Flow = 10.0,
HasOscillation = true,
Id = default,
IdCategory = 2,
IdFeedRegulator = 1,
Pressure = 10.0,
PressureIdle = 10.0,
PressureSp = 10.0,
RotorSpeed = 10.0,
RotorTorque = 10.0,
RotorTorqueSp = 10.0,
RotorTorqueLimitMax = 10.0,
Speed = 10.0
}
};
private static readonly WellOperationCategory category = new()
{
Id = 2,
IdParent = null,
Name = "Категория 2"
};
private readonly IDataSaubStatRepository dataSaubStatRepository; var telemetryIds = dataSaubStat.Select(stat => stat.IdTelemetry).ToArray();
public DataSaubStatRepositoryTest(WebAppFactoryFixture factory) : base(factory) //act
{ var result = await dataSaubStatRepository.GetLastsAsync(telemetryIds, CancellationToken.None);
dataSaubStatRepository = scope.ServiceProvider.GetRequiredService<IDataSaubStatRepository>();
}
[Fact] //assert
public async Task GetLastDatesAsync_returns_success() var expected = dataSaubStat.Max(stat => stat.DateEnd).ToOffset(timeZoneOffset);
{ var actual = result.First().DateEnd;
//prepare Assert.True((expected - actual).Ticks == 0.0);
dbContext.CleanupDbSet<DataSaubStat>(); }
dbContext.CleanupDbSet<WellOperationCategory>();
var dbSetSaubStat = dbContext.Set<DataSaubStat>(); [Fact]
var dbSetCategories = dbContext.Set<WellOperationCategory>(); public async Task InsertRangeAsync_returns_success()
{
//arrange
var telemetry = await dbContext.Telemetries.FirstAsync();
var timeZoneOffset = TimeSpan.FromHours(telemetry.TimeZone.Hours);
var dataSaubStat = CreateDataSaubStat(telemetry.Id, timeZoneOffset);
var expectedDtos = dataSaubStat.Select(entity => ConvertToDto(entity, timeZoneOffset));
var entities = statDtos.Select(stat => ConvertToEntity(stat)); //act
await dataSaubStatRepository.InsertRangeAsync(expectedDtos, CancellationToken.None);
dbSetCategories.Add(category); //assert
dbContext.SaveChanges(); var entities = await dbContext.DataSaubStat.ToArrayAsync();
var actualDtos = entities.Select(entity => ConvertToDto(entity, timeZoneOffset));
dbSetSaubStat.AddRange(entities); var excludedProps = new[]
dbContext.SaveChanges(); {
nameof(DataSaubStat.Telemetry),
nameof(DataSaubStat.Id),
nameof(DataSaubStat.OperationCategory)
};
//act foreach (var actualDto in actualDtos)
var telemetryIds = statDtos.Select(stat => stat.IdTelemetry).ToArray(); {
var result = await dataSaubStatRepository.GetLastsAsync(telemetryIds, CancellationToken.None); var statDto = expectedDtos.FirstOrDefault(stat => stat.DateEnd == actualDto.DateEnd &&
stat.DateStart == actualDto.DateStart);
var expected = statDtos.Max(stat => stat.DateEnd); MatchHelper.Match(actualDto, statDto, excludedProps);
var actual = result.First().DateEnd; }
}
//assert private static IEnumerable<DataSaubStat> CreateDataSaubStat(int idTelemetry, TimeSpan timeZoneOffset) => new DataSaubStat[]
Assert.True((expected - actual).Ticks == 0.0); {
} new()
{
IdTelemetry = idTelemetry,
DateEnd = new DateTimeOffset(2024, 1, 1, 20, 25, 0, timeZoneOffset).ToUniversalTime(),
DateStart = new DateTimeOffset(2024, 1, 1, 20, 15, 0, timeZoneOffset).ToUniversalTime(),
AxialLoad = 10.0,
AxialLoadLimitMax = 10.0,
AxialLoadSp = 10.0,
BlockSpeedSp = 1000,
DepthEnd = 10.0,
DepthStart = 5.0,
EnabledSubsystems = 1,
Flow = 10.0,
HasOscillation = true,
IdCategory = WellOperationCategory.IdSlide,
IdFeedRegulator = 1,
Pressure = 10.0,
PressureIdle = 10.0,
PressureSp = 10.0,
RotorSpeed = 9.0,
RotorTorque = 9.0,
RotorTorqueSp = 9.0,
RotorTorqueLimitMax = 9.0,
Speed = 10.0
},
new()
{
IdTelemetry = idTelemetry,
DateEnd = new DateTimeOffset(2024, 2, 2, 20, 25, 0, timeZoneOffset).ToUniversalTime(),
DateStart = new DateTimeOffset(2024, 2, 2, 20, 15, 0, timeZoneOffset).ToUniversalTime(),
AxialLoad = 10.0,
AxialLoadLimitMax = 10.0,
AxialLoadSp = 10.0,
BlockSpeedSp = 1000,
DepthEnd = 10.0,
DepthStart = 5.0,
EnabledSubsystems = 1,
Flow = 10.0,
HasOscillation = true,
IdCategory = WellOperationCategory.IdSlide,
IdFeedRegulator = 1,
Pressure = 10.0,
PressureIdle = 10.0,
PressureSp = 10.0,
RotorSpeed = 10.0,
RotorTorque = 10.0,
RotorTorqueSp = 10.0,
RotorTorqueLimitMax = 10.0,
Speed = 10.0
}
};
[Fact] private static DataSaubStatDto ConvertToDto(DataSaubStat entity, TimeSpan timeZoneOffset)
public async Task InsertRangeAsync_returns_success() {
{ var dto = entity.Adapt<DataSaubStatDto>();
//prepare dto.DateStart = dto.DateStart.ToOffset(timeZoneOffset);
dbContext.CleanupDbSet<DataSaubStat>(); dto.DateEnd = dto.DateEnd.ToOffset(timeZoneOffset);
var dbSet = dbContext.Set<DataSaubStat>();
var dbSetCategories = dbContext.Set<WellOperationCategory>();
dbSetCategories.Add(category);
dbContext.SaveChanges(); return dto;
}
//act }
var result = await dataSaubStatRepository.InsertRangeAsync(statDtos, CancellationToken.None);
//assert
Assert.Equal(statDtos.Length, result);
var statDtosFromDb = dbSet.Select(stat => ConvertToDto(stat, timeSpan)).ToArray();
var excludedProps = new[] {
nameof(DataSaubStat.Telemetry),
nameof(DataSaubStat.Id),
nameof(DataSaubStat.OperationCategory)
};
foreach (var statDtoFromDb in statDtosFromDb)
{
var statDto = statDtos
.Where(stat => stat.DateStart == statDtoFromDb.DateStart)
.Where(stat => stat.DateEnd == statDtoFromDb.DateEnd)
.FirstOrDefault();
MatchHelper.Match(statDtoFromDb, statDto, excludedProps);
}
}
private static DataSaubStat ConvertToEntity(DataSaubStatDto stat)
{
var entity = stat.Adapt<DataSaubStat>();
entity.DateStart = entity.DateStart.ToUniversalTime();
entity.DateEnd = entity.DateEnd.ToUniversalTime();
return entity;
}
private static DataSaubStatDto ConvertToDto(DataSaubStat stat, TimeSpan timeSpan)
{
var dto = stat.Adapt<DataSaubStatDto>();
dto.DateStart = dto.DateStart.ToOffset(timeSpan);
dto.DateEnd = dto.DateEnd.ToOffset(timeSpan);
return dto;
}
}

View File

@ -1,4 +1,3 @@
using AsbCloudDb;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.Mvc.Testing;
@ -8,87 +7,85 @@ using Microsoft.Extensions.DependencyInjection;
using Refit; using Refit;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text.Json; using System.Text.Json;
using AsbCloudDb;
using AsbCloudWebApi.IntegrationTests.Converters; using AsbCloudWebApi.IntegrationTests.Converters;
using Xunit;
namespace AsbCloudWebApi.IntegrationTests; namespace AsbCloudWebApi.IntegrationTests;
public class WebAppFactoryFixture : WebApplicationFactory<Startup>, public class WebAppFactoryFixture : WebApplicationFactory<Startup>
IAsyncLifetime
{ {
private static readonly JsonSerializerOptions jsonSerializerOptions = new() private static readonly JsonSerializerOptions JsonSerializerOptions = new()
{ {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true, PropertyNameCaseInsensitive = true,
Converters = { new ValidationResultConverter() } Converters = { new ValidationResultConverter() }
}; };
private static readonly RefitSettings refitSettings = new RefitSettings(new SystemTextJsonContentSerializer(jsonSerializerOptions)); private static readonly RefitSettings RefitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions));
protected override void ConfigureWebHost(IWebHostBuilder builder) private readonly string connectionString;
public WebAppFactoryFixture()
{ {
var configuration = new ConfigurationBuilder() var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json") .AddJsonFile("appsettings.Tests.json")
.Build(); .Build();
var connectionString = configuration.GetConnectionString("TestConnection")!;
var dbConnection = configuration.GetSection("DbConnection").Get<DbConnection>()!;
builder.ConfigureServices(services => connectionString = dbConnection.GetConnectionString();
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{ {
var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(DbContextOptions<AsbCloudDbContext>)); var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<AsbCloudDbContext>));
if (descriptor is not null) if (descriptor != null)
services.Remove(descriptor); services.Remove(descriptor);
services.AddDbContext<AsbCloudDbContext>(options => services.AddDbContext<AsbCloudDbContext>(options =>
options.UseNpgsql(connectionString)); options.UseNpgsql(connectionString));
var serviceProvider = services.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
var scopedServices = scope.ServiceProvider;
var dbContext = scopedServices.GetRequiredService<AsbCloudDbContext>();
dbContext.Database.EnsureCreatedAndMigrated();
dbContext.Deposits.AddRange(Data.Defaults.Deposits);
dbContext.SaveChanges();
}); });
} }
public async Task InitializeAsync() public override async ValueTask DisposeAsync()
{ {
using var scope = Services.CreateScope(); var dbContext = new AsbCloudDbContext(
var scopedServices = scope.ServiceProvider; new DbContextOptionsBuilder<AsbCloudDbContext>()
var dbContext = scopedServices.GetRequiredService<AsbCloudDbContext>(); .UseNpgsql(connectionString)
dbContext.Database.EnsureDeleted(); .Options);
dbContext.Database.EnsureCreatedAndMigrated();
await FillBaseDatasets(dbContext); await dbContext.Database.EnsureDeletedAsync();
} }
private static async Task FillBaseDatasets(AsbCloudDbContext dbContext) public T GetAuthorizedHttpClient<T>(string uriSuffix)
{
dbContext.AddRange(Data.Defaults.Deposits);
dbContext.AddRange(Data.Defaults.Clusters);
dbContext.AddRange(Data.Defaults.Wells);
dbContext.AddRange(Data.Defaults.RelationsCompanyWell);
dbContext.AddRange(Data.Defaults.Telemetries);
dbContext.AddRange(Data.Defaults.Drillers);
dbContext.AddRange(Data.Defaults.WellOperations);
await dbContext.SaveChangesAsync();
}
public new async Task DisposeAsync()
{ {
using var scope = Services.CreateScope(); var httpClient = GetAuthorizedHttpClient();
var scopedServices = scope.ServiceProvider; if (string.IsNullOrEmpty(uriSuffix))
var dbContext = scopedServices.GetRequiredService<AsbCloudDbContext>(); return RestService.For<T>(httpClient, RefitSettings);
await dbContext.Database.EnsureDeletedAsync();
}
public HttpClient GetAuthorizedHttpClient() if (httpClient.BaseAddress is not null)
{ httpClient.BaseAddress = new Uri(httpClient.BaseAddress, uriSuffix);
var httpClient = CreateClient();
var jwtToken = ApiTokenHelper.GetAdminUserToken();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
return httpClient;
}
public T GetAuthorizedHttpClient<T>(string uriSuffix) return RestService.For<T>(httpClient, RefitSettings);
{ }
var httpClient = GetAuthorizedHttpClient();
if (!string.IsNullOrEmpty(uriSuffix)) private HttpClient GetAuthorizedHttpClient()
if(httpClient.BaseAddress is not null) {
httpClient.BaseAddress = new Uri(httpClient.BaseAddress, uriSuffix); var httpClient = CreateClient();
return RestService.For<T>(httpClient, refitSettings); var jwtToken = ApiTokenHelper.GetAdminUserToken();
} httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
return httpClient;
}
} }

View File

@ -6,12 +6,15 @@ using NSubstitute;
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Requests.ExportOptions;
using Xunit; using Xunit;
namespace AsbCloudWebApi.Tests.Services.Trajectory namespace AsbCloudWebApi.Tests.Services.Trajectory
{ {
public class TrajectoryExportTest public class TrajectoryExportTest
{ {
private const int idWell = 4;
private IWellService wellService; private IWellService wellService;
private readonly ITrajectoryEditableRepository<TrajectoryGeoPlanDto> trajectoryPlanRepository; private readonly ITrajectoryEditableRepository<TrajectoryGeoPlanDto> trajectoryPlanRepository;
private readonly TrajectoryPlanExportService trajectoryPlanExportService; private readonly TrajectoryPlanExportService trajectoryPlanExportService;
@ -22,8 +25,6 @@ namespace AsbCloudWebApi.Tests.Services.Trajectory
private readonly ITrajectoryNnbRepository trajectoryFactNnbRepository; private readonly ITrajectoryNnbRepository trajectoryFactNnbRepository;
private readonly TrajectoryFactNnbExportService trajectoryFactNnbExportService; private readonly TrajectoryFactNnbExportService trajectoryFactNnbExportService;
private readonly int idWell = 4;
private readonly TrajectoryGeoPlanDto[] trajectoryPlanRows = new TrajectoryGeoPlanDto[2] { private readonly TrajectoryGeoPlanDto[] trajectoryPlanRows = new TrajectoryGeoPlanDto[2] {
new TrajectoryGeoPlanDto() { new TrajectoryGeoPlanDto() {
Id = 1, Id = 1,
@ -80,6 +81,8 @@ namespace AsbCloudWebApi.Tests.Services.Trajectory
}, },
}; };
private readonly WellRelatedExportRequest exportOptions = new(idWell);
public TrajectoryExportTest() public TrajectoryExportTest()
{ {
wellService = Substitute.For<IWellService>(); wellService = Substitute.For<IWellService>();
@ -98,10 +101,9 @@ namespace AsbCloudWebApi.Tests.Services.Trajectory
{ {
trajectoryPlanRepository.GetAsync(idWell, CancellationToken.None) trajectoryPlanRepository.GetAsync(idWell, CancellationToken.None)
.Returns(trajectoryPlanRows); .Returns(trajectoryPlanRows);
var stream = await trajectoryPlanExportService.ExportAsync(idWell, CancellationToken.None); var stream = await trajectoryPlanExportService.ExportAsync(exportOptions, CancellationToken.None);
Assert.True(stream.Length > 0); Assert.True(stream.File.Length > 0);
} }
[Fact] [Fact]
@ -109,9 +111,9 @@ namespace AsbCloudWebApi.Tests.Services.Trajectory
{ {
trajectoryFactManualReposirory.GetAsync(idWell, CancellationToken.None) trajectoryFactManualReposirory.GetAsync(idWell, CancellationToken.None)
.Returns(trajectoryFactRows); .Returns(trajectoryFactRows);
var stream = await trajectoryFactManualExportService.ExportAsync(idWell, CancellationToken.None); var stream = await trajectoryFactManualExportService.ExportAsync(exportOptions, CancellationToken.None);
Assert.True(stream.Length > 0); Assert.True(stream.File.Length > 0);
} }
[Fact] [Fact]
@ -119,9 +121,9 @@ namespace AsbCloudWebApi.Tests.Services.Trajectory
{ {
trajectoryFactNnbRepository.GetAsync(idWell, CancellationToken.None) trajectoryFactNnbRepository.GetAsync(idWell, CancellationToken.None)
.Returns(trajectoryFactRows); .Returns(trajectoryFactRows);
var stream = await trajectoryFactNnbExportService.ExportAsync(idWell, CancellationToken.None); var stream = await trajectoryFactNnbExportService.ExportAsync(exportOptions, CancellationToken.None);
Assert.True(stream.Length > 0); Assert.True(stream.File.Length > 0);
} }
} }
} }

View File

@ -1,7 +1,6 @@
using System.Linq; using System.Linq;
using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Requests.ParserOptions;
using AsbCloudInfrastructure.Services.Parser;
using AsbCloudInfrastructure.Services.Trajectory.Parser; using AsbCloudInfrastructure.Services.Trajectory.Parser;
using Xunit; using Xunit;
@ -11,9 +10,11 @@ public class TrajectoryParserTest
{ {
private const string UsingTemplateFile = "AsbCloudWebApi.Tests.Services.Trajectory.Templates"; private const string UsingTemplateFile = "AsbCloudWebApi.Tests.Services.Trajectory.Templates";
private readonly WellRelatedParserRequest options = new(1);
private readonly TrajectoryPlanParser trajectoryPlanParser = new(); private readonly TrajectoryPlanParser trajectoryPlanParser = new();
private readonly TrajectoryFactManualParser trajectoryFactManualParser = new(); private readonly TrajectoryFactManualParser trajectoryFactManualParser = new();
[Fact] [Fact]
public void Parse_trajectory_plan() public void Parse_trajectory_plan()
{ {
@ -22,8 +23,8 @@ public class TrajectoryParserTest
if (stream is null) if (stream is null)
Assert.Fail("Файла для импорта не существует"); Assert.Fail("Файла для импорта не существует");
var trajectoryRows = trajectoryPlanParser.Parse(stream, IParserOptionsRequest.Empty()); var trajectoryRows = trajectoryPlanParser.Parse(stream, options);
Assert.Equal(3, trajectoryRows.Item.Count()); Assert.Equal(3, trajectoryRows.Item.Count());
} }
@ -37,7 +38,7 @@ public class TrajectoryParserTest
if (stream is null) if (stream is null)
Assert.Fail("Файла для импорта не существует"); Assert.Fail("Файла для импорта не существует");
var trajectoryRows = trajectoryFactManualParser.Parse(stream, IParserOptionsRequest.Empty()); var trajectoryRows = trajectoryFactManualParser.Parse(stream, options);
Assert.Equal(4, trajectoryRows.Item.Count()); Assert.Equal(4, trajectoryRows.Item.Count());
} }

View File

@ -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<WellSectionTypeDto> wellSectionTypeRepository
= Substitute.For<ICrudRepository<WellSectionTypeDto>>();
private IWellOperationCategoryRepository wellOperationCategoryRepository
= Substitute.For<IWellOperationCategoryRepository>();
private IWellOperationRepository wellOperationRepository
= Substitute.For<IWellOperationRepository>();
private readonly static IEnumerable<WellOperationCategoryDto> operationCategories = new List<WellOperationCategoryDto>()
{
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<WellSectionTypeDto> sectionTypes = new List<WellSectionTypeDto>()
{
new() {Id = 2, Caption = "Направление", Order = 0},
new() {Id = 3, Caption = "Кондуктор", Order = 1},
new() {Id = 31, Caption = "Техническая колонна", Order = 2}
};
private readonly static IEnumerable<WellOperationDataDto> wellOperations1 = new List<WellOperationDataDto>()
{
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<WellOperationDataDto> wellOperations2 = new List<WellOperationDataDto>()
{
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<WellOperationDataDto> wellOperations3 = new List<WellOperationDataDto>()
{
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<WellOperationDataDto> wellOperations4 = new List<WellOperationDataDto>()
{
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<CancellationToken>())
.Returns(sectionTypes);
wellOperationCategoryRepository.Get(Arg.Any<bool>())
.Returns(operationCategories);
service = new WellCompositeOperationService(
wellSectionTypeRepository,
wellOperationCategoryRepository,
wellOperationRepository);
}
/// <summary>
/// На вход подаются 2 операции с одинаковыми секциями (id = 2), но разными категориями (ids = 5096, 5008) и ключами скважин (ids = 55, 64)
/// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная
/// Операция должна иметь категорию 5013 для всех трех скважин
/// </summary>
/// <returns></returns>
[Fact]
public async Task GetAsync_return_composite_and_others_with_category_5013()
{
// arrange
wellOperationRepository.GetAsync(Arg.Any<WellsOperationRequest>(), Arg.Any<CancellationToken>())
.Returns(wellOperations1);
var idsWell = new List<int>() { 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);
}
/// <summary>
/// На вход подаются 2 операции с одинаковыми секциями (id = 2) и категориями (id = 5003), но разными ключами скважин (ids = 55, 64)
/// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная
/// Операция композитной скважины должна содержать данные той операции, которая содержит минимальный duration_hours
/// </summary>
/// <returns></returns>
[Fact]
public async Task GetAsync_return_composite_with_minimum_depth_start()
{
// arrange
wellOperationRepository.GetAsync(Arg.Any<WellsOperationRequest>(), Arg.Any<CancellationToken>())
.Returns(wellOperations2);
var idsWell = new List<int>() { 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);
}
/// <summary>
/// На вход подаются 2 операции с одинаковыми секциями (id = 3) и категориями (id = 5036), но разными ключами скважин (ids = 55, 64)
/// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная
/// Операция композитной скважины должна содержать данные той операции, которая содержит минимальный duration_hours
/// </summary>
/// <returns></returns>
[Fact]
public async Task GetAsync_return_data3()
{
// arrange
wellOperationRepository.GetAsync(Arg.Any<WellsOperationRequest>(), Arg.Any<CancellationToken>())
.Returns(wellOperations3);
var idsWell = new List<int>() { 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);
}
/// <summary>
/// На вход подаются 3 операции с одинаковыми секциями (id = 31), но разными категориями (ids = 5012, 5083) и ключами скважин (ids = 55, 64)
/// Метод возвращает список из одной операции в разрезе 3-х скважин: 2 текущие скважины и одна композитная
/// Операция композитной скважины должна содержать:
/// данные той операции, которая содержит минимальный duration_hours
/// категорию с ключом 5013
/// Операции по скважине с ключом 55 должны объединиться в одну,
/// при этом их длительность складывается, а depth_start берется минимальный из двух
/// </summary>
/// <returns></returns>
[Fact]
public async Task GetAsync_return_data4()
{
// arrange
wellOperationRepository.GetAsync(Arg.Any<WellsOperationRequest>(), Arg.Any<CancellationToken>())
.Returns(wellOperations4);
var idsWell = new List<int>() { 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);
}
/// <summary>
/// На вход подаются список разных операций с разными ключами скважин (ids = 55, 64)
/// Метод возвращает список из 4-х операций в разрезе 3-х скважин: 2 текущие скважины и одна композитная
/// Операция композитной скважины должна содержать глубину забоя = 1372
/// </summary>
/// <returns></returns>
[Fact]
public async Task GetAsync_return_data5()
{
// arrange
var wellOperations = new List<WellOperationDataDto>();
wellOperations.AddRange(wellOperations1);
wellOperations.AddRange(wellOperations2);
wellOperations.AddRange(wellOperations3);
wellOperations.AddRange(wellOperations4);
wellOperationRepository.GetAsync(Arg.Any<WellsOperationRequest>(), Arg.Any<CancellationToken>())
.Returns(wellOperations);
var idsWell = new List<int>() { 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);
}
}
}

View File

@ -13,9 +13,9 @@ using AsbCloudApp.Services;
using System.Linq; using System.Linq;
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Requests.ParserOptions;
using AsbCloudInfrastructure.Services.Parser;
using AsbCloudApp.Data.ProcessMaps; using AsbCloudApp.Data.ProcessMaps;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using AsbCloudApp.Requests.ExportOptions;
namespace AsbCloudWebApi.Controllers.ProcessMaps; namespace AsbCloudWebApi.Controllers.ProcessMaps;
@ -30,15 +30,18 @@ public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase
{ {
private readonly IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository; private readonly IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository;
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly ParserExcelService<TDto> parserService; private readonly IParserService<TDto, WellRelatedParserRequest> parserService;
private readonly IExportService<WellRelatedExportRequest> processMapPlanExportService;
protected ProcessMapPlanBaseController(IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository, protected ProcessMapPlanBaseController(IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository,
IWellService wellService, IWellService wellService,
ParserExcelService<TDto> parserService) IParserService<TDto, WellRelatedParserRequest> parserService,
IExportService<WellRelatedExportRequest> processMapPlanExportService)
{ {
this.repository = repository; this.repository = repository;
this.wellService = wellService; this.wellService = wellService;
this.parserService = parserService; this.parserService = parserService;
this.processMapPlanExportService = processMapPlanExportService;
} }
protected abstract string TemplateFileName { get; } protected abstract string TemplateFileName { get; }
@ -213,7 +216,7 @@ public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<ParserResultDto<TDto>>> Parse(int idWell, public async Task<ActionResult<ParserResultDto<TDto>>> Parse(int idWell,
[Required] IFormFile file, [Required] IFormFile file,
CancellationToken token) CancellationToken token)
{ {
await AssertUserHasAccessToWell(idWell, token); await AssertUserHasAccessToWell(idWell, token);
@ -222,9 +225,8 @@ public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase
try try
{ {
var dto = parserService.Parse(stream, IParserOptionsRequest.Empty()); var options = new WellRelatedParserRequest(idWell);
foreach (var item in dto.Item) var dto = parserService.Parse(stream, options);
item.Item.IdWell = idWell;
return Ok(dto); return Ok(dto);
} }
@ -267,6 +269,22 @@ public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase
throw new ForbidException("Нет доступа к скважине"); throw new ForbidException("Нет доступа к скважине");
return idUser; return idUser;
} }
/// <summary>
/// Формируем excel файл с текущими строками РТК
/// </summary>
/// <param name="idWell">id скважины</param>
/// <param name="token"></param>
/// <returns>Запрашиваемый файл</returns>
[HttpGet("export")]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> ExportAsync([FromRoute] int idWell, CancellationToken token)
{
var exportOptions = new WellRelatedExportRequest(idWell);
var (fileName, file) = await processMapPlanExportService.ExportAsync(exportOptions, token);
return File(file, "application/octet-stream", fileName);
}
/// <summary> /// <summary>
/// returns user id or throw /// returns user id or throw

View File

@ -2,6 +2,7 @@
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.ProcessMapPlan.Export;
using AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; using AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
namespace AsbCloudWebApi.Controllers.ProcessMaps; namespace AsbCloudWebApi.Controllers.ProcessMaps;
@ -13,8 +14,9 @@ public class ProcessMapPlanDrillingController : ProcessMapPlanBaseController<Pro
{ {
public ProcessMapPlanDrillingController(IChangeLogRepository<ProcessMapPlanDrillingDto, ProcessMapPlanBaseRequestWithWell> repository, public ProcessMapPlanDrillingController(IChangeLogRepository<ProcessMapPlanDrillingDto, ProcessMapPlanBaseRequestWithWell> repository,
IWellService wellService, IWellService wellService,
ProcessMapPlanDrillingParser parserService) ProcessMapPlanDrillingParser parserService,
: base(repository, wellService, parserService) ProcessMapPlanDrillingExportService processMapPlanExportService)
: base(repository, wellService, parserService, processMapPlanExportService)
{ {
} }

View File

@ -2,6 +2,7 @@
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.ProcessMapPlan.Export;
using AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; using AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
namespace AsbCloudWebApi.Controllers.ProcessMaps; namespace AsbCloudWebApi.Controllers.ProcessMaps;
@ -13,8 +14,9 @@ public class ProcessMapPlanReamController : ProcessMapPlanBaseController<Process
{ {
public ProcessMapPlanReamController(IChangeLogRepository<ProcessMapPlanReamDto, ProcessMapPlanBaseRequestWithWell> repository, public ProcessMapPlanReamController(IChangeLogRepository<ProcessMapPlanReamDto, ProcessMapPlanBaseRequestWithWell> repository,
IWellService wellService, IWellService wellService,
ProcessMapPlanReamParser parserService) ProcessMapPlanReamParser parserService,
: base(repository, wellService, parserService) ProcessMapPlanReamExportService processMapPlanExportService)
: base(repository, wellService, parserService, processMapPlanExportService)
{ {
} }

View File

@ -1,14 +1,14 @@
using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.Trajectory.Export;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudInfrastructure.Services.Trajectory.Export;
namespace AsbCloudWebApi.Controllers.Trajectory namespace AsbCloudWebApi.Controllers.Trajectory
{ {
@ -21,13 +21,13 @@ namespace AsbCloudWebApi.Controllers.Trajectory
public abstract class TrajectoryController<TDto> : ControllerBase public abstract class TrajectoryController<TDto> : ControllerBase
where TDto : TrajectoryGeoDto where TDto : TrajectoryGeoDto
{ {
protected abstract string fileName { get; } protected abstract string TemplateFileName { get; }
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly TrajectoryExportService<TDto> trajectoryExportService; private readonly TrajectoryExportService<TDto> trajectoryExportService;
private readonly ITrajectoryRepository<TDto> trajectoryRepository; private readonly ITrajectoryRepository<TDto> trajectoryRepository;
public TrajectoryController(IWellService wellService, protected TrajectoryController(IWellService wellService,
TrajectoryExportService<TDto> trajectoryExportService, TrajectoryExportService<TDto> trajectoryExportService,
ITrajectoryRepository<TDto> trajectoryRepository) ITrajectoryRepository<TDto> trajectoryRepository)
{ {
@ -50,9 +50,10 @@ namespace AsbCloudWebApi.Controllers.Trajectory
if (!await CanUserAccessToWellAsync(idWell, if (!await CanUserAccessToWellAsync(idWell,
token).ConfigureAwait(false)) token).ConfigureAwait(false))
return Forbid(); return Forbid();
var stream = await trajectoryExportService.ExportAsync(idWell, token);
var fileName = await trajectoryExportService.GetFileNameAsync(idWell, token); var exportOptions = new WellRelatedExportRequest(idWell);
return File(stream, "application/octet-stream", fileName); var (fileName, file) = await trajectoryExportService.ExportAsync(exportOptions, token);
return File(file, "application/octet-stream", fileName);
} }
/// <summary> /// <summary>

View File

@ -1,8 +1,6 @@
using System; using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.Trajectory.Export;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -12,7 +10,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Requests.ParserOptions;
using AsbCloudInfrastructure.Services.Parser; using AsbCloudInfrastructure.Services.Trajectory.Export;
namespace AsbCloudWebApi.Controllers.Trajectory namespace AsbCloudWebApi.Controllers.Trajectory
{ {
@ -25,11 +23,11 @@ namespace AsbCloudWebApi.Controllers.Trajectory
public abstract class TrajectoryEditableController<TDto> : TrajectoryController<TDto> public abstract class TrajectoryEditableController<TDto> : TrajectoryController<TDto>
where TDto : TrajectoryGeoDto where TDto : TrajectoryGeoDto
{ {
private readonly ParserExcelService<TDto> parserService; private readonly IParserService<TDto, WellRelatedParserRequest> parserService;
private readonly ITrajectoryEditableRepository<TDto> trajectoryRepository; private readonly ITrajectoryEditableRepository<TDto> trajectoryRepository;
protected TrajectoryEditableController(IWellService wellService, protected TrajectoryEditableController(IWellService wellService,
ParserExcelService<TDto> parserService, IParserService<TDto, WellRelatedParserRequest> parserService,
TrajectoryExportService<TDto> trajectoryExportService, TrajectoryExportService<TDto> trajectoryExportService,
ITrajectoryEditableRepository<TDto> trajectoryRepository) ITrajectoryEditableRepository<TDto> trajectoryRepository)
: base(wellService, trajectoryExportService, trajectoryRepository) : base(wellService, trajectoryExportService, trajectoryRepository)
@ -49,7 +47,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory
public IActionResult GetTemplate() public IActionResult GetTemplate()
{ {
var stream = parserService.GetTemplateFile(); var stream = parserService.GetTemplateFile();
return File(stream, "application/octet-stream", fileName); return File(stream, "application/octet-stream", TemplateFileName);
} }
/// <summary> /// <summary>
@ -78,7 +76,9 @@ namespace AsbCloudWebApi.Controllers.Trajectory
try try
{ {
var dto = parserService.Parse(stream, IParserOptionsRequest.Empty()); var options = new WellRelatedParserRequest(idWell);
var dto = parserService.Parse(stream, options);
return Ok(dto); return Ok(dto);
} }
catch (FileFormatException ex) catch (FileFormatException ex)

View File

@ -14,7 +14,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory;
[Route("api/well/{idWell}/[controller]")] [Route("api/well/{idWell}/[controller]")]
public class TrajectoryFactManualController : TrajectoryEditableController<TrajectoryGeoFactDto> public class TrajectoryFactManualController : TrajectoryEditableController<TrajectoryGeoFactDto>
{ {
protected override string fileName => "ЕЦП_шаблон_файлаактическая_траектория.xlsx"; protected override string TemplateFileName => "ЕЦП_шаблон_файлаактическая_траектория.xlsx";
public TrajectoryFactManualController(IWellService wellService, public TrajectoryFactManualController(IWellService wellService,
TrajectoryFactManualExportService trajectoryExportService, TrajectoryFactManualExportService trajectoryExportService,

View File

@ -15,7 +15,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory;
[Route("api/well/{idWell}/[controller]")] [Route("api/well/{idWell}/[controller]")]
public class TrajectoryFactNnbController : TrajectoryController<TrajectoryGeoFactDto> public class TrajectoryFactNnbController : TrajectoryController<TrajectoryGeoFactDto>
{ {
protected override string fileName => "ЕЦП_шаблон_файлаактическая_ннбраектория.xlsx"; protected override string TemplateFileName => "ЕЦП_шаблон_файлаактическая_ннбраектория.xlsx";
public TrajectoryFactNnbController( public TrajectoryFactNnbController(
ITrajectoryNnbRepository trajectoryNnbRepository, ITrajectoryNnbRepository trajectoryNnbRepository,
TrajectoryFactNnbExportService trajectoryExportService, TrajectoryFactNnbExportService trajectoryExportService,

View File

@ -20,7 +20,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory
{ {
private readonly TrajectoryService trajectoryVisualizationService; private readonly TrajectoryService trajectoryVisualizationService;
protected override string fileName => "ЕЦП_шаблон_файла_плановая_траектория.xlsx"; protected override string TemplateFileName => "ЕЦП_шаблон_файла_плановая_траектория.xlsx";
public TrajectoryPlanController(IWellService wellService, public TrajectoryPlanController(IWellService wellService,
TrajectoryPlanParser parserService, TrajectoryPlanParser parserService,

View File

@ -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<IDictionary<int, WellOperationDataDto>>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetAsync([FromQuery] IEnumerable<int> 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<bool> 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;
}
}
}

View File

@ -7,10 +7,7 @@ using System.IO;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions; using AsbCloudApp.Exceptions;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudInfrastructure.Services.Parser;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Mvc; namespace Microsoft.AspNetCore.Mvc;

View File

@ -1,13 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
namespace AsbCloudWebApi namespace AsbCloudWebApi
{ {
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class PermissionAttribute : Attribute public class PermissionAttribute : Attribute
{ {
public static SortedSet<string> Registered { get; } = new SortedSet<string>(); public static ConcurrentBag<string> Registered { get; } = new();
public string Name { get; set; } = null!; public string Name { get; set; } = null!;

View File

@ -5,13 +5,18 @@ namespace AsbCloudWebApi
{ {
public static class ProtobufModel public static class ProtobufModel
{ {
private static readonly object SyncRoot = new();
public static void EnshureRegistered() public static void EnshureRegistered()
{ {
EnshureRegisteredDataSpin(); lock (SyncRoot)
EnshureRegisteredDataSaub(); {
EnshureRegisteredWITS(); EnshureRegisteredDataSpin();
EnshureRegisteredWirelineRunOutBaseDto(); EnshureRegisteredDataSaub();
EnshureRegisteredWirelineRunOutDto(); EnshureRegisteredWITS();
EnshureRegisteredWirelineRunOutBaseDto();
EnshureRegisteredWirelineRunOutDto();
}
} }
private static void EnshureRegisteredWirelineRunOutBaseDto() private static void EnshureRegisteredWirelineRunOutBaseDto()

View File

@ -0,0 +1,8 @@
{
"DbConnection": {
"Host": "localhost",
"Port": 5432,
"Username": "postgres",
"Password": "q"
}
}