forked from ddrilling/AsbCloudServer
merge dev to rtk-report-export
This commit is contained in:
commit
8f271eac0a
@ -19,6 +19,11 @@ public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelat
|
||||
[Range(1, int.MaxValue, ErrorMessage = "Id секции скважины не может быть меньше 1")]
|
||||
public int IdWellSectionType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Название секции
|
||||
/// </summary>
|
||||
public string? Section { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Глубина по стволу от, м
|
||||
/// <para>
|
||||
@ -41,6 +46,6 @@ public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelat
|
||||
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if(DepthEnd <= DepthStart)
|
||||
yield return new ("глубина окончания должна быть больше глубины начала", new string[] {nameof(DepthEnd), nameof(DepthStart) });
|
||||
yield return new ("Глубина окончания должна быть больше глубины начала", new string[] {nameof(DepthEnd), nameof(DepthStart) });
|
||||
}
|
||||
}
|
@ -12,6 +12,11 @@ public class ProcessMapPlanDrillingDto : ProcessMapPlanBaseDto
|
||||
/// </summary>
|
||||
[Range(1, 2, ErrorMessage = "Id режима должен быть либо 1-ротор либо 2-слайд")]
|
||||
public int IdMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Название режима бурения
|
||||
/// </summary>
|
||||
public string? Mode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Осевая нагрузка, т план
|
||||
|
@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace AsbCloudApp.Data.Trajectory
|
||||
{
|
||||
/// <summary>
|
||||
/// Базовая географическая траектория
|
||||
/// </summary>
|
||||
public abstract class TrajectoryGeoDto : IId
|
||||
public abstract class TrajectoryGeoDto : IId, IValidatableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// ИД строки с координатами
|
||||
@ -49,5 +52,11 @@ namespace AsbCloudApp.Data.Trajectory
|
||||
/// ИД пользователя
|
||||
/// </summary>
|
||||
public int IdUser { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
return Enumerable.Empty<ValidationResult>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
|
||||
@ -10,10 +8,8 @@ namespace AsbCloudApp.Services;
|
||||
/// Сервис парсинга
|
||||
/// </summary>
|
||||
/// <typeparam name="TDto"></typeparam>
|
||||
/// <typeparam name="TOptions"></typeparam>
|
||||
public interface IParserService<TDto, in TOptions> : IParserService
|
||||
public interface IParserService<TDto> : IParserService
|
||||
where TDto : class, IId
|
||||
where TOptions : IParserOptionsRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Распарсить файл
|
||||
@ -21,7 +17,8 @@ public interface IParserService<TDto, in TOptions> : IParserService
|
||||
/// <param name="file"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
ParserResultDto<TDto> Parse(Stream file, TOptions options);
|
||||
ParserResultDto<TDto> Parse<TOptions>(Stream file, TOptions options)
|
||||
where TOptions : IParserOptionsRequest;
|
||||
|
||||
/// <summary>
|
||||
/// Получение шаблона для заполнения
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
@ -7,6 +8,7 @@ namespace AsbCloudApp.Services.ProcessMaps;
|
||||
/// <summary>
|
||||
/// Сервис импорта РТК
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public interface IProcessMapPlanImportService
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -37,6 +37,7 @@
|
||||
<EmbeddedResource Include="Services\DetectOperations\DetectOperations.xlsx" />
|
||||
<EmbeddedResource Include="Services\DailyReport\DailyReportTemplate.xlsx" />
|
||||
<EmbeddedResource Include="Services\DrillTestReport\DrillTestReportTemplate.xlsx" />
|
||||
<EmbeddedResource Include="Services\ProcessMapPlan\Templates\ProcessMapPlanDrillingTemplate.xlsx" />
|
||||
<EmbeddedResource Include="Services\ProcessMaps\Report\ProcessMapReportDataSaubStatTemplate.xlsx" />
|
||||
<EmbeddedResource Include="Services\Trajectory\Templates\TrajectoryFactNnbTemplate.xlsx" />
|
||||
<EmbeddedResource Include="Services\Trajectory\Templates\TrajectoryFactManualTemplate.xlsx" />
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.DrillTestReport;
|
||||
using AsbCloudApp.Data.Manuals;
|
||||
@ -45,6 +46,8 @@ using AsbCloudDb.Model.WellSections;
|
||||
using AsbCloudInfrastructure.Services.ProcessMaps;
|
||||
using AsbCloudApp.Data.ProcessMapPlan;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudInfrastructure.Services.Parser;
|
||||
using AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
namespace AsbCloudInfrastructure
|
||||
@ -206,8 +209,6 @@ namespace AsbCloudInfrastructure
|
||||
services.AddTransient<TrajectoryPlanExportService>();
|
||||
services.AddTransient<TrajectoryFactManualExportService>();
|
||||
services.AddTransient<TrajectoryFactNnbExportService>();
|
||||
services.AddTransient<TrajectoryPlanParserService>();
|
||||
services.AddTransient<TrajectoryFactManualParserService>();
|
||||
services.AddTransient<IWellOperationRepository, WellOperationRepository>();
|
||||
services.AddTransient<IDailyReportService, DailyReportService>();
|
||||
services.AddTransient<IDetectedOperationService, DetectedOperationService>();
|
||||
@ -335,8 +336,10 @@ namespace AsbCloudInfrastructure
|
||||
services.AddTransient<IWellOperationCategoryRepository, WellOperationCategoryRepository>();
|
||||
services.AddTransient<IDetectedOperationRepository, DetectedOperationRepository>();
|
||||
|
||||
services.AddSingleton<ParserServiceFactory>();
|
||||
|
||||
services.AddTransient<TrajectoryPlanParser>();
|
||||
services.AddTransient<TrajectoryFactManualParser>();
|
||||
services.AddTransient<ProcessMapPlanDrillingParser>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
@ -1,72 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
using AsbCloudApp.Services;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace AsbCloudInfrastructure;
|
||||
|
||||
public abstract class ParserServiceBase<TDto, TOptions> : IParserService<TDto, TOptions>
|
||||
where TDto : class, IId
|
||||
where TOptions : IParserOptionsRequest
|
||||
{
|
||||
protected readonly IServiceProvider serviceProvider;
|
||||
|
||||
protected ParserServiceBase(IServiceProvider serviceProvider)
|
||||
{
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public abstract ParserResultDto<TDto> Parse(Stream file, TOptions options);
|
||||
public abstract Stream GetTemplateFile();
|
||||
|
||||
protected virtual ParserResultDto<TDto> ParseExcelSheet(IXLWorksheet sheet,
|
||||
Func<IXLRow, ValidationResultDto<TDto>> parseRow,
|
||||
int columnCount,
|
||||
int headerRowsCount = 0)
|
||||
{
|
||||
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnCount)
|
||||
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов.");
|
||||
|
||||
var count = sheet.RowsUsed().Count() - headerRowsCount;
|
||||
|
||||
if (count > 1024)
|
||||
throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество строк.");
|
||||
|
||||
if (count <= 0)
|
||||
return new ParserResultDto<TDto>();
|
||||
|
||||
var dtos = new List<ValidationResultDto<TDto>>(count);
|
||||
var warnings = new List<ValidationResult>();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var row = sheet.Row(1 + i + headerRowsCount);
|
||||
|
||||
try
|
||||
{
|
||||
var dto = parseRow.Invoke(row);
|
||||
dtos.Add(dto);
|
||||
}
|
||||
catch (FileFormatException ex)
|
||||
{
|
||||
var warning = new ValidationResult(ex.Message);
|
||||
warnings.Add(warning);
|
||||
}
|
||||
}
|
||||
|
||||
var parserResult = new ParserResultDto<TDto>
|
||||
{
|
||||
Item = dtos
|
||||
};
|
||||
|
||||
if (warnings.Any())
|
||||
parserResult.Warnings = warnings;
|
||||
|
||||
return parserResult;
|
||||
}
|
||||
}
|
@ -30,7 +30,6 @@ public abstract class ChangeLogRepositoryAbstract<TDto, TEntity, TRequest> : ICh
|
||||
var result = 0;
|
||||
if (dtos.Any())
|
||||
{
|
||||
using var transaction = await db.Database.BeginTransactionAsync(token);
|
||||
var entities = dtos.Select(Convert);
|
||||
var creation = DateTimeOffset.UtcNow;
|
||||
var dbSet = db.Set<TEntity>();
|
||||
@ -47,7 +46,6 @@ public abstract class ChangeLogRepositoryAbstract<TDto, TEntity, TRequest> : ICh
|
||||
}
|
||||
|
||||
result += await SaveChangesWithExceptionHandling(token);
|
||||
await transaction.CommitAsync(token);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -82,30 +80,39 @@ public abstract class ChangeLogRepositoryAbstract<TDto, TEntity, TRequest> : ICh
|
||||
}
|
||||
|
||||
using var transaction = db.Database.BeginTransaction();
|
||||
foreach (var entity in entitiesToDelete)
|
||||
try
|
||||
{
|
||||
entity.IdState = ChangeLogAbstract.IdStateReplaced;
|
||||
entity.Obsolete = updateTime;
|
||||
entity.IdEditor = idUser;
|
||||
}
|
||||
result += await db.SaveChangesAsync(token);
|
||||
foreach (var entity in entitiesToDelete)
|
||||
{
|
||||
entity.IdState = ChangeLogAbstract.IdStateReplaced;
|
||||
entity.Obsolete = updateTime;
|
||||
entity.IdEditor = idUser;
|
||||
}
|
||||
result += await db.SaveChangesAsync(token);
|
||||
|
||||
var entitiesNew = dtos.Select(Convert);
|
||||
foreach (var entity in entitiesNew)
|
||||
var entitiesNew = dtos.Select(Convert);
|
||||
foreach (var entity in entitiesNew)
|
||||
{
|
||||
entity.IdPrevious = entity.Id;
|
||||
entity.Id = default;
|
||||
entity.Creation = updateTime;
|
||||
entity.IdAuthor = idUser;
|
||||
entity.Obsolete = null;
|
||||
entity.IdEditor = null;
|
||||
entity.IdState = ChangeLogAbstract.IdStateActual;
|
||||
dbSet.Add(entity);
|
||||
}
|
||||
|
||||
result += await SaveChangesWithExceptionHandling(token);
|
||||
|
||||
await transaction.CommitAsync(token);
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
entity.IdPrevious = entity.Id;
|
||||
entity.Id = default;
|
||||
entity.Creation = updateTime;
|
||||
entity.IdAuthor = idUser;
|
||||
entity.Obsolete = null;
|
||||
entity.IdEditor = null;
|
||||
entity.IdState = ChangeLogAbstract.IdStateActual;
|
||||
dbSet.Add(entity);
|
||||
await transaction.RollbackAsync(token);
|
||||
throw;
|
||||
}
|
||||
|
||||
result += await SaveChangesWithExceptionHandling(token);
|
||||
await transaction.CommitAsync(token);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<int> UpdateOrInsertRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
|
||||
@ -146,10 +153,19 @@ public abstract class ChangeLogRepositoryAbstract<TDto, TEntity, TRequest> : ICh
|
||||
{
|
||||
var result = 0;
|
||||
using var transaction = await db.Database.BeginTransactionAsync(token);
|
||||
result += await Clear(idUser, request, token);
|
||||
result += await InsertRange(idUser, dtos, token);
|
||||
await transaction.CommitAsync(token);
|
||||
return result;
|
||||
try
|
||||
{
|
||||
result += await Clear(idUser, request, token);
|
||||
result += await InsertRange(idUser, dtos, token);
|
||||
|
||||
await transaction.CommitAsync(token);
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await transaction.RollbackAsync(token);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> DeleteRange(int idUser, IEnumerable<int> ids, CancellationToken token)
|
||||
|
94
AsbCloudInfrastructure/Services/Parser/Cell.cs
Normal file
94
AsbCloudInfrastructure/Services/Parser/Cell.cs
Normal file
@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Parser;
|
||||
|
||||
public class Cell
|
||||
{
|
||||
private static IDictionary<Type, Func<IXLCell, object?>> converters = new Dictionary<Type, Func<IXLCell, object?>>()
|
||||
{
|
||||
{ typeof(bool), cell => cell.GetBoolean() },
|
||||
{ typeof(double), cell => cell.GetValue<double>() },
|
||||
{ typeof(float), cell => cell.GetValue<float>() },
|
||||
{ typeof(long), cell => cell.GetValue<long>() },
|
||||
{ typeof(ulong), cell => cell.GetValue<ulong>() },
|
||||
{ typeof(int), cell => cell.GetValue<int>() },
|
||||
{ typeof(uint), cell => cell.GetValue<uint>() },
|
||||
{ typeof(short), cell => cell.GetValue<short>() },
|
||||
{ typeof(ushort), cell => cell.GetValue<ushort>() },
|
||||
{ typeof(string), cell => cell.GetString() },
|
||||
|
||||
{
|
||||
typeof(DateTime), cell =>
|
||||
{
|
||||
if (cell.DataType == XLDataType.DateTime)
|
||||
return cell.GetDateTime();
|
||||
|
||||
var stringValue = cell.GetString();
|
||||
return DateTime.Parse(stringValue);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
typeof(DateTimeOffset), cell =>
|
||||
{
|
||||
var stringValue = cell.GetString();
|
||||
return DateTimeOffset.Parse(stringValue);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
typeof(DateOnly), cell =>
|
||||
{
|
||||
var stringValue = cell.GetString();
|
||||
return DateOnly.Parse(stringValue);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
typeof(TimeOnly), cell =>
|
||||
{
|
||||
var stringValue = cell.GetString();
|
||||
return TimeOnly.Parse(stringValue);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
typeof(TimeSpan), cell =>
|
||||
{
|
||||
if (cell.DataType == XLDataType.TimeSpan)
|
||||
return cell.GetTimeSpan();
|
||||
|
||||
var stringValue = cell.GetString();
|
||||
return TimeSpan.Parse(stringValue);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
private readonly Type type;
|
||||
|
||||
public Cell(int columnNumber,
|
||||
Type type)
|
||||
{
|
||||
ColumnNumber = columnNumber;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int ColumnNumber { get; }
|
||||
|
||||
public object? GetValueFromCell(IXLCell cell)
|
||||
{
|
||||
try
|
||||
{
|
||||
return converters[type].Invoke(cell);
|
||||
}
|
||||
catch
|
||||
{
|
||||
var message = string.Format(XLExtentions.InvalidValueTemplate, cell.Worksheet.Name, cell.Address.RowNumber,
|
||||
cell.Address.ColumnNumber);
|
||||
throw new FileFormatException(message);
|
||||
}
|
||||
}
|
||||
}
|
135
AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs
Normal file
135
AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs
Normal file
@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
using AsbCloudApp.Services;
|
||||
using ClosedXML.Excel;
|
||||
using Mapster;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Parser;
|
||||
|
||||
public abstract class ParserExcelService<TDto> : IParserService<TDto>
|
||||
where TDto : class, IValidatableObject, IId
|
||||
{
|
||||
protected abstract string SheetName { get; }
|
||||
|
||||
protected virtual int HeaderRowsCount => 0;
|
||||
|
||||
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);
|
||||
var sheet = workbook.GetWorksheet(SheetName);
|
||||
var dtos = ParseExcelSheet(sheet);
|
||||
return dtos;
|
||||
}
|
||||
|
||||
public virtual Stream GetTemplateFile() =>
|
||||
Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateFileName)
|
||||
?? throw new ArgumentNullException($"Файл '{TemplateFileName}' не найден");
|
||||
|
||||
|
||||
protected virtual IDictionary<string, object?> ParseRow(IXLRow xlRow)
|
||||
{
|
||||
var cells = Cells.ToDictionary(x => x.Key, x =>
|
||||
{
|
||||
var columnNumber = x.Value.ColumnNumber;
|
||||
var xlCell = xlRow.Cell(columnNumber);
|
||||
var cellValue = x.Value.GetValueFromCell(xlCell);
|
||||
return cellValue;
|
||||
});
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
protected virtual TDto BuildDto(IDictionary<string, object?> row, int rowNumber)
|
||||
{
|
||||
var dto = row.Adapt<TDto>();
|
||||
return dto;
|
||||
}
|
||||
|
||||
private ValidationResultDto<TDto> Validate(TDto dto, int rowNumber)
|
||||
{
|
||||
var validationResults = new List<ValidationResult>();
|
||||
|
||||
var isValid = dto.Validate(validationResults);
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
var validDto = new ValidationResultDto<TDto>
|
||||
{
|
||||
Item = dto
|
||||
};
|
||||
|
||||
return validDto;
|
||||
}
|
||||
|
||||
var columnsDict = Cells.ToDictionary(x => x.Key, x => x.Value.ColumnNumber);
|
||||
|
||||
var invalidDto = new ValidationResultDto<TDto>
|
||||
{
|
||||
Item = dto,
|
||||
Warnings = validationResults
|
||||
.SelectMany(v => v.MemberNames
|
||||
.Where(columnsDict.ContainsKey)
|
||||
.Select(m =>
|
||||
{
|
||||
var columnNumber = columnsDict[m];
|
||||
var errorMessage = v.ErrorMessage;
|
||||
var warningMessage = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, rowNumber, columnNumber,
|
||||
errorMessage);
|
||||
var warning = new ValidationResult(warningMessage, new[] { m });
|
||||
return warning;
|
||||
}))
|
||||
};
|
||||
|
||||
return invalidDto;
|
||||
}
|
||||
|
||||
protected virtual ParserResultDto<TDto> ParseExcelSheet(IXLWorksheet sheet)
|
||||
{
|
||||
var count = sheet.RowsUsed().Count() - HeaderRowsCount;
|
||||
if (count <= 0)
|
||||
return new ParserResultDto<TDto>();
|
||||
|
||||
var valiationResults = new List<ValidationResultDto<TDto>>(count);
|
||||
var warnings = new List<ValidationResult>();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var xlRow = sheet.Row(1 + i + HeaderRowsCount);
|
||||
var rowNumber = xlRow.RowNumber();
|
||||
|
||||
try
|
||||
{
|
||||
var row = ParseRow(xlRow);
|
||||
var dto = BuildDto(row, rowNumber);
|
||||
var validationResult = Validate(dto, rowNumber);
|
||||
valiationResults.Add(validationResult);
|
||||
}
|
||||
catch (FileFormatException ex)
|
||||
{
|
||||
var warning = new ValidationResult(ex.Message);
|
||||
warnings.Add(warning);
|
||||
}
|
||||
}
|
||||
|
||||
var parserResult = new ParserResultDto<TDto>
|
||||
{
|
||||
Item = valiationResults
|
||||
};
|
||||
|
||||
if (warnings.Any())
|
||||
parserResult.Warnings = warnings;
|
||||
|
||||
return parserResult;
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services;
|
||||
|
||||
public class ParserServiceFactory
|
||||
{
|
||||
public const int IdTrajectoryFactManualParserService = 1;
|
||||
public const int IdTrajectoryPlanParserService = 2;
|
||||
|
||||
private readonly IDictionary<int, Func<IParserService>> parsers;
|
||||
|
||||
public ParserServiceFactory(IServiceProvider serviceProvider)
|
||||
{
|
||||
parsers = new Dictionary<int, Func<IParserService>>
|
||||
{
|
||||
{ IdTrajectoryPlanParserService, () => new TrajectoryPlanParserService(serviceProvider) },
|
||||
{ IdTrajectoryFactManualParserService, () => new TrajectoryFactManualParserService(serviceProvider) }
|
||||
};
|
||||
}
|
||||
|
||||
public IParserService<TDto, TOptions> Create<TDto, TOptions>(int idParserService)
|
||||
where TDto : class, IId
|
||||
where TOptions : IParserOptionsRequest
|
||||
{
|
||||
if (!parsers.TryGetValue(idParserService, out var parserService))
|
||||
throw new ArgumentNullException(nameof(idParserService), "Не правильный идентификатор парсера");
|
||||
|
||||
return parserService.Invoke() as IParserService<TDto, TOptions>
|
||||
?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа");
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.ProcessMapPlan;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudInfrastructure.Services.Parser;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
|
||||
|
||||
public class ProcessMapPlanDrillingParser : ProcessMapPlanParser<ProcessMapPlanDrillingDto>
|
||||
{
|
||||
private readonly IEnumerable<WellSectionTypeDto> sections;
|
||||
|
||||
public ProcessMapPlanDrillingParser(IWellOperationRepository wellOperationRepository)
|
||||
{
|
||||
sections = wellOperationRepository.GetSectionTypes();
|
||||
}
|
||||
|
||||
protected override string SheetName => "План";
|
||||
protected override string TemplateFileName => "ProcessMapPlanDrillingTemplate.xlsx";
|
||||
|
||||
private const int ColumnSection = 1;
|
||||
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)
|
||||
{
|
||||
var dto = base.BuildDto(row, rowNumber);
|
||||
|
||||
var section = sections.FirstOrDefault(s =>
|
||||
string.Equals(s.Caption.Trim(), dto.Section?.Trim(), StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
if (section is null)
|
||||
{
|
||||
var message = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, rowNumber, ColumnSection,
|
||||
"Указана некорректная секция");
|
||||
throw new FileFormatException(message);
|
||||
}
|
||||
|
||||
var idMode = GetIdMode(dto.Mode);
|
||||
|
||||
if (idMode is null)
|
||||
{
|
||||
var message = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, rowNumber, ColumnSection,
|
||||
"Указан некорректный режим бурения");
|
||||
throw new FileFormatException(message);
|
||||
}
|
||||
|
||||
dto.IdWellSectionType = section.Id;
|
||||
dto.IdMode = idMode.Value;
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using AsbCloudApp.Data.ProcessMapPlan;
|
||||
using AsbCloudInfrastructure.Services.Parser;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
|
||||
|
||||
public abstract class ProcessMapPlanParser<TDto> : ParserExcelService<TDto>
|
||||
where TDto : ProcessMapPlanBaseDto
|
||||
{
|
||||
protected override int HeaderRowsCount => 2;
|
||||
|
||||
protected static int? GetIdMode(string? modeName) =>
|
||||
modeName?.Trim().ToLower() switch
|
||||
{
|
||||
"ротор" => 1,
|
||||
"слайд" => 2,
|
||||
_ => null
|
||||
};
|
||||
}
|
Binary file not shown.
@ -18,6 +18,8 @@ namespace AsbCloudInfrastructure.Services.ProcessMaps.WellDrilling;
|
||||
/*
|
||||
* password for ProcessMapImportTemplate.xlsx is ASB2020!
|
||||
*/
|
||||
|
||||
[Obsolete]
|
||||
public class ProcessMapPlanImportWellDrillingService : IProcessMapPlanImportService
|
||||
{
|
||||
private readonly IProcessMapPlanRepository<ProcessMapPlanWellDrillingDto> processMapPlanWellDrillingRepository;
|
||||
|
@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using AsbCloudInfrastructure.Services.Parser;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
public class TrajectoryFactManualParser : ParserExcelService<TrajectoryGeoFactDto>
|
||||
{
|
||||
protected override string SheetName => "Фактическая траектория";
|
||||
|
||||
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)) }
|
||||
};
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
using System;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
public class TrajectoryFactManualParserService : TrajectoryParserService<TrajectoryGeoFactDto>
|
||||
{
|
||||
protected override string SheetName => "Фактическая траектория";
|
||||
protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx";
|
||||
|
||||
public TrajectoryFactManualParserService(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ValidationResultDto<TrajectoryGeoFactDto> ParseRow(IXLRow row)
|
||||
{
|
||||
var trajectoryRow = new TrajectoryGeoFactDto
|
||||
{
|
||||
WellboreDepth = row.Cell(1).GetCellValue<double>(),
|
||||
ZenithAngle = row.Cell(2).GetCellValue<double>(),
|
||||
AzimuthGeo = row.Cell(3).GetCellValue<double>(),
|
||||
AzimuthMagnetic = row.Cell(4).GetCellValue<double>(),
|
||||
VerticalDepth = row.Cell(5).GetCellValue<double>(),
|
||||
Comment = row.Cell(6).GetCellValue<string?>()
|
||||
};
|
||||
|
||||
//TODO: Добавить валидацию модели
|
||||
|
||||
var validationResult = new ValidationResultDto<TrajectoryGeoFactDto>
|
||||
{
|
||||
Item = trajectoryRow
|
||||
};
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
public abstract class TrajectoryParserService<T> : ParserServiceBase<T, IParserOptionsRequest>
|
||||
where T : TrajectoryGeoDto
|
||||
{
|
||||
private const int HeaderRowsCount = 2;
|
||||
private const int ColumnCount = 6;
|
||||
|
||||
protected TrajectoryParserService(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract string SheetName { get; }
|
||||
|
||||
protected abstract string TemplateFileName { get; }
|
||||
|
||||
protected abstract ValidationResultDto<T> ParseRow(IXLRow row);
|
||||
|
||||
public override Stream GetTemplateFile() =>
|
||||
Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateFileName)
|
||||
?? throw new ArgumentNullException($"Файл '{TemplateFileName}' не найден");
|
||||
|
||||
public override ParserResultDto<T> Parse(Stream file, IParserOptionsRequest options)
|
||||
{
|
||||
using var workbook = new XLWorkbook(file);
|
||||
|
||||
var sheet = workbook.GetWorksheet(SheetName);
|
||||
|
||||
var trajectoryRows = ParseExcelSheet(sheet, ParseRow, ColumnCount, HeaderRowsCount);
|
||||
return trajectoryRows;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using AsbCloudInfrastructure.Services.Parser;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
public class TrajectoryPlanParser : ParserExcelService<TrajectoryGeoPlanDto>
|
||||
{
|
||||
protected override string SheetName => "Плановая траектория";
|
||||
|
||||
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)) }
|
||||
};
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
public class TrajectoryPlanParserService : TrajectoryParserService<TrajectoryGeoPlanDto>
|
||||
{
|
||||
protected override string SheetName => "Плановая траектория";
|
||||
protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx";
|
||||
|
||||
public TrajectoryPlanParserService(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ValidationResultDto<TrajectoryGeoPlanDto> ParseRow(IXLRow row)
|
||||
{
|
||||
var trajectoryRow = new TrajectoryGeoPlanDto
|
||||
{
|
||||
WellboreDepth = row.Cell(1).GetCellValue<double>(),
|
||||
ZenithAngle = row.Cell(2).GetCellValue<double>(),
|
||||
AzimuthGeo = row.Cell(3).GetCellValue<double>(),
|
||||
AzimuthMagnetic = row.Cell(4).GetCellValue<double>(),
|
||||
VerticalDepth = row.Cell(5).GetCellValue<double>(),
|
||||
Radius = row.Cell(6).GetCellValue<double>(),
|
||||
Comment = row.Cell(7).GetCellValue<string?>()
|
||||
};
|
||||
|
||||
//TODO: Добавить валидацию модели
|
||||
|
||||
var validationResult = new ValidationResultDto<TrajectoryGeoPlanDto>
|
||||
{
|
||||
Item = trajectoryRow
|
||||
};
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
17
AsbCloudInfrastructure/ValidationExtensions.cs
Normal file
17
AsbCloudInfrastructure/ValidationExtensions.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace AsbCloudInfrastructure;
|
||||
|
||||
public static class ValidationExtensions
|
||||
{
|
||||
public static bool Validate(this IValidatableObject validatableObject, ICollection<ValidationResult> validationResults)
|
||||
{
|
||||
var validationContext = new ValidationContext(validatableObject, serviceProvider: null, items: null);
|
||||
|
||||
foreach (var validationResult in validatableObject.Validate(validationContext))
|
||||
validationResults.Add(validationResult);
|
||||
|
||||
return Validator.TryValidateObject(validatableObject, validationContext, validationResults, true);
|
||||
}
|
||||
}
|
@ -7,9 +7,13 @@ namespace AsbCloudInfrastructure;
|
||||
|
||||
public static class XLExtentions
|
||||
{
|
||||
public static IXLWorksheet GetWorksheet(this IXLWorkbook workbook, string sheetName) =>
|
||||
workbook.Worksheets.FirstOrDefault(ws => string.Equals(ws.Name.Trim(), sheetName.Trim(), StringComparison.CurrentCultureIgnoreCase))
|
||||
?? throw new FileFormatException($"Книга excel не содержит листа {sheetName}.");
|
||||
public const string ProblemDetailsTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. {3}";
|
||||
public const string NotFoundSheetTemplate = "Книга excel не содержит листа {0}";
|
||||
public const string InvalidValueTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. Содержит некорректное значение";
|
||||
|
||||
public static IXLWorksheet GetWorksheet(this IXLWorkbook workbook, string sheetName) =>
|
||||
workbook.Worksheets.FirstOrDefault(ws => string.Equals(ws.Name.Trim(), sheetName.Trim(), StringComparison.CurrentCultureIgnoreCase))
|
||||
?? throw new FileFormatException(string.Format(NotFoundSheetTemplate, sheetName));
|
||||
|
||||
public static IXLCell SetCellValue<T>(this IXLCell cell, T value, string? format = null)
|
||||
{
|
||||
@ -37,12 +41,12 @@ public static class XLExtentions
|
||||
if (cell.IsEmpty() && default(T) == null)
|
||||
return default;
|
||||
|
||||
return cell.GetValue<T>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new FileFormatException(
|
||||
$"Лист '{cell.Worksheet.Name}'. {cell.Address.RowNumber} строка содержит некорректное значение в {cell.Address.ColumnNumber} столбце");
|
||||
}
|
||||
}
|
||||
return cell.GetValue<T>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
var message = string.Format(InvalidValueTemplate, cell.Worksheet.Name, cell.Address.RowNumber, cell.Address.ColumnNumber);
|
||||
throw new FileFormatException(message);
|
||||
}
|
||||
}
|
||||
}
|
@ -22,4 +22,9 @@
|
||||
<ProjectReference Include="..\AsbCloudWebApi\AsbCloudWebApi.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Controllers\ProcessMapPlan\Files\ProcessMapPlanDrillingInvalid.xlsx" />
|
||||
<EmbeddedResource Include="Controllers\ProcessMapPlan\Files\ProcessMapPlanDrillingValid.xlsx" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
25
AsbCloudWebApi.IntegrationTests/AssemblyExtensions.cs
Normal file
25
AsbCloudWebApi.IntegrationTests/AssemblyExtensions.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace AsbCloudWebApi.IntegrationTests;
|
||||
|
||||
internal static class AssemblyExtensions
|
||||
{
|
||||
internal static Stream GetFileCopyStream(this Assembly assembly, string templateName)
|
||||
{
|
||||
var resourceName = assembly
|
||||
.GetManifestResourceNames()
|
||||
.FirstOrDefault(n => n.EndsWith(templateName));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(resourceName))
|
||||
throw new ArgumentNullException(nameof(resourceName));
|
||||
|
||||
using var stream = Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream(resourceName);
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
stream?.CopyTo(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
return memoryStream;
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using AsbCloudApp.Data.ProcessMapPlan;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.ProcessMapPlan;
|
||||
using AsbCloudApp.Requests;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Refit;
|
||||
|
||||
namespace AsbCloudWebApi.IntegrationTests.Clients;
|
||||
@ -32,4 +32,8 @@ public interface IProcessMapPlanDrillingClient
|
||||
|
||||
[Put(BaseRoute)]
|
||||
Task<IApiResponse<int>> UpdateOrInsertRange(int idWell, IEnumerable<ProcessMapPlanDrillingDto> dtos);
|
||||
|
||||
[Multipart]
|
||||
[Post(BaseRoute + "/parse")]
|
||||
Task<IApiResponse<ParserResultDto<ProcessMapPlanDrillingDto>>> Parse(int idWell, [AliasAs("files")] IEnumerable<StreamPart> streams);
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1,31 +1,35 @@
|
||||
using AsbCloudApp.Data.ProcessMapPlan;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudDb.Model.ProcessMapPlan;
|
||||
using AsbCloudDb.Model.ProcessMaps;
|
||||
using AsbCloudWebApi.IntegrationTests.Clients;
|
||||
using Mapster;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using AsbCloudDb.Model.ProcessMaps;
|
||||
using AsbCloudWebApi.IntegrationTests.Data;
|
||||
using Refit;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.IntegrationTests.Controllers;
|
||||
namespace AsbCloudWebApi.IntegrationTests.Controllers.ProcessMapPlan;
|
||||
|
||||
public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
|
||||
{
|
||||
private IProcessMapPlanDrillingClient client;
|
||||
private readonly ProcessMapPlanDrillingDto dto = new (){
|
||||
Id = 0,
|
||||
Creation = new(),
|
||||
Obsolete = null,
|
||||
IdState = 0,
|
||||
IdPrevious = null,
|
||||
|
||||
|
||||
IdWell = 1,
|
||||
IdWellSectionType = 1,
|
||||
Section = "Кондуктор",
|
||||
IdWellSectionType = 3,
|
||||
DepthStart = 0.5,
|
||||
DepthEnd = 1.5,
|
||||
|
||||
IdMode = 1,
|
||||
Mode = "Ротор",
|
||||
AxialLoadPlan = 2.718281,
|
||||
AxialLoadLimitMax = 3.1415926,
|
||||
DeltaPressurePlan = 4,
|
||||
@ -73,6 +77,8 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
|
||||
Comment = "это тестовая запись",
|
||||
};
|
||||
|
||||
private IProcessMapPlanDrillingClient client;
|
||||
|
||||
public ProcessMapPlanDrillingControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||
{
|
||||
dbContext.CleanupDbSet<ProcessMapPlanDrilling>();
|
||||
@ -109,6 +115,8 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
|
||||
nameof(ProcessMapPlanDrillingDto.IdState),
|
||||
nameof(ProcessMapPlanDrillingDto.Author),
|
||||
nameof(ProcessMapPlanDrillingDto.Creation),
|
||||
nameof(ProcessMapPlanDrillingDto.Mode),
|
||||
nameof(ProcessMapPlanDrillingDto.Section)
|
||||
};
|
||||
MatchHelper.Match(expected, actual, excludeProps);
|
||||
}
|
||||
@ -556,4 +564,59 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
|
||||
};
|
||||
MatchHelper.Match(expected, actual, excludeProps);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_returns_success()
|
||||
{
|
||||
//arrange
|
||||
const string fileName = "ProcessMapPlanDrillingValid.xlsx";
|
||||
var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName);
|
||||
|
||||
//act
|
||||
var streamPart = new StreamPart(stream, fileName, "application/octet-stream");
|
||||
var response = await client.Parse(Defaults.Wells[0].Id, new[] { streamPart });
|
||||
|
||||
//assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
var parserResult = response.Content;
|
||||
|
||||
Assert.NotNull(parserResult);
|
||||
Assert.Single(parserResult.Item);
|
||||
Assert.True(parserResult.IsValid);
|
||||
|
||||
var row = parserResult.Item.First();
|
||||
var dtoActual = row.Item;
|
||||
|
||||
Assert.True(row.IsValid);
|
||||
|
||||
var excludeProps = new[] { nameof(ProcessMapPlanDrillingDto.IdWell) };
|
||||
MatchHelper.Match(dto, dtoActual, excludeProps);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_returns_success_for_result_with_warnings()
|
||||
{
|
||||
//arrange
|
||||
const string fileName = "ProcessMapPlanDrillingInvalid.xlsx";
|
||||
var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName);
|
||||
|
||||
//act
|
||||
var streamPart = new StreamPart(stream, fileName, "application/octet-stream");
|
||||
var response = await client.Parse(Defaults.Wells[0].Id, new[] { streamPart });
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
var parserResult = response.Content;
|
||||
|
||||
Assert.NotNull(parserResult);
|
||||
Assert.False(parserResult.IsValid);
|
||||
Assert.Single(parserResult.Warnings);
|
||||
Assert.Single(parserResult.Item);
|
||||
|
||||
var row = parserResult.Item.First();
|
||||
|
||||
Assert.False(row.IsValid);
|
||||
Assert.Equal(2, row.Warnings.Count());
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AsbCloudWebApi.IntegrationTests.Converters;
|
||||
|
||||
public class ValidationResultConverter : JsonConverter<ValidationResult>
|
||||
{
|
||||
public override ValidationResult Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.StartObject)
|
||||
{
|
||||
throw new JsonException("Expected the start of an object.");
|
||||
}
|
||||
|
||||
string? errorMessage = null;
|
||||
List<string>? memberNames = null;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.EndObject)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (reader.TokenType != JsonTokenType.PropertyName)
|
||||
{
|
||||
throw new JsonException($"Unexpected token type: {reader.TokenType}");
|
||||
}
|
||||
|
||||
var propertyName = reader.GetString();
|
||||
reader.Read();
|
||||
|
||||
switch (propertyName)
|
||||
{
|
||||
case "errorMessage":
|
||||
errorMessage = reader.GetString();
|
||||
break;
|
||||
case "memberNames":
|
||||
if (reader.TokenType != JsonTokenType.StartArray)
|
||||
{
|
||||
throw new JsonException("Expected the start of an array for 'memberNames'.");
|
||||
}
|
||||
memberNames = new List<string>();
|
||||
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
|
||||
{
|
||||
memberNames.Add(reader.GetString() ?? string.Empty);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorMessage == null)
|
||||
{
|
||||
throw new JsonException("Missing 'errorMessage' property.");
|
||||
}
|
||||
|
||||
return new ValidationResult(errorMessage, memberNames ?? Enumerable.Empty<string>());
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ValidationResult value, JsonSerializerOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Refit;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using AsbCloudWebApi.IntegrationTests.Converters;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.IntegrationTests;
|
||||
@ -18,7 +19,8 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>,
|
||||
private static readonly JsonSerializerOptions jsonSerializerOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNameCaseInsensitive = true
|
||||
PropertyNameCaseInsensitive = true,
|
||||
Converters = { new ValidationResultConverter() }
|
||||
};
|
||||
|
||||
private static readonly RefitSettings refitSettings = new RefitSettings(new SystemTextJsonContentSerializer(jsonSerializerOptions));
|
||||
|
@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
using AsbCloudInfrastructure.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using AsbCloudInfrastructure.Services.Parser;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services.Trajectory;
|
||||
@ -12,21 +10,10 @@ namespace AsbCloudWebApi.Tests.Services.Trajectory;
|
||||
public class TrajectoryParserTest
|
||||
{
|
||||
private const string UsingTemplateFile = "AsbCloudWebApi.Tests.Services.Trajectory.Templates";
|
||||
|
||||
private readonly TrajectoryPlanParser trajectoryPlanParser = new();
|
||||
private readonly TrajectoryFactManualParser trajectoryFactManualParser = new();
|
||||
|
||||
private readonly IServiceProvider serviceProviderMock = Substitute.For<IServiceProvider, ISupportRequiredService>();
|
||||
private readonly IServiceScope serviceScopeMock = Substitute.For<IServiceScope>();
|
||||
private readonly IServiceScopeFactory serviceScopeFactoryMock = Substitute.For<IServiceScopeFactory>();
|
||||
|
||||
private readonly ParserServiceFactory parserServiceFactory;
|
||||
|
||||
public TrajectoryParserTest()
|
||||
{
|
||||
serviceScopeFactoryMock.CreateScope().Returns(serviceScopeMock);
|
||||
((ISupportRequiredService)serviceProviderMock).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactoryMock);
|
||||
|
||||
parserServiceFactory = new ParserServiceFactory(serviceProviderMock);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_trajectory_plan()
|
||||
{
|
||||
@ -35,11 +22,8 @@ public class TrajectoryParserTest
|
||||
|
||||
if (stream is null)
|
||||
Assert.Fail("Файла для импорта не существует");
|
||||
|
||||
var parserService = parserServiceFactory.Create<TrajectoryGeoPlanDto, IParserOptionsRequest>(
|
||||
ParserServiceFactory.IdTrajectoryPlanParserService);
|
||||
|
||||
var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty());
|
||||
var trajectoryRows = trajectoryPlanParser.Parse(stream, IParserOptionsRequest.Empty());
|
||||
|
||||
Assert.Equal(3, trajectoryRows.Item.Count());
|
||||
}
|
||||
@ -53,10 +37,7 @@ public class TrajectoryParserTest
|
||||
if (stream is null)
|
||||
Assert.Fail("Файла для импорта не существует");
|
||||
|
||||
var parserService = parserServiceFactory.Create<TrajectoryGeoFactDto, IParserOptionsRequest>(
|
||||
ParserServiceFactory.IdTrajectoryFactManualParserService);
|
||||
|
||||
var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty());
|
||||
var trajectoryRows = trajectoryFactManualParser.Parse(stream, IParserOptionsRequest.Empty());
|
||||
|
||||
Assert.Equal(4, trajectoryRows.Item.Count());
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using AsbCloudInfrastructure;
|
||||
using ClosedXML.Excel;
|
||||
using Xunit;
|
||||
@ -112,6 +113,16 @@ public class XLExtensionsTests
|
||||
Assert.Equal(DateTimeKind.Unspecified, actualValue.Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCellValue_returns_exception()
|
||||
{
|
||||
//arrange
|
||||
SetCellValue("test");
|
||||
|
||||
//assert
|
||||
Assert.Throws<FileFormatException>(() => GetCell(cellUsed).GetCellValue<double>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCellValue_returns_nullable()
|
||||
{
|
||||
|
@ -1,13 +0,0 @@
|
||||
using System.IO;
|
||||
using AsbCloudApp.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AsbCloudWebApi.Controllers.Interfaces;
|
||||
|
||||
public interface IControllerWithParser<TDto, in TOptions>
|
||||
where TDto : class, IId
|
||||
{
|
||||
ActionResult<ParserResultDto<TDto>> Parse(Stream file, TOptions options);
|
||||
|
||||
IActionResult GetTemplate();
|
||||
}
|
@ -9,8 +9,12 @@ using Microsoft.AspNetCore.Http;
|
||||
using AsbCloudApp.Exceptions;
|
||||
using AsbCloudApp.Requests;
|
||||
using System;
|
||||
using System.IO;
|
||||
using AsbCloudApp.Services;
|
||||
using System.Linq;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
using AsbCloudInfrastructure.Services.Parser;
|
||||
|
||||
namespace AsbCloudWebApi.Controllers.ProcessMapPlan;
|
||||
|
||||
@ -21,18 +25,24 @@ namespace AsbCloudWebApi.Controllers.ProcessMapPlan;
|
||||
[Route("api/well/{idWell}/[controller]")]
|
||||
[Authorize]
|
||||
public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase
|
||||
where TDto : ProcessMapPlanBaseDto
|
||||
where TDto : ProcessMapPlanBaseDto
|
||||
{
|
||||
private readonly IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository;
|
||||
private readonly IWellService wellService;
|
||||
private readonly IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository;
|
||||
private readonly IWellService wellService;
|
||||
private readonly ParserExcelService<TDto> parserService;
|
||||
|
||||
protected ProcessMapPlanBaseController(IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository,
|
||||
IWellService wellService,
|
||||
ParserExcelService<TDto> parserService)
|
||||
{
|
||||
this.repository = repository;
|
||||
this.wellService = wellService;
|
||||
this.parserService = parserService;
|
||||
}
|
||||
|
||||
protected abstract string TemplateFileName { get; }
|
||||
|
||||
public ProcessMapPlanBaseController(IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository, IWellService wellService)
|
||||
{
|
||||
this.repository = repository;
|
||||
this.wellService = wellService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Добавление
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
@ -190,6 +200,49 @@ public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase
|
||||
var result = await repository.UpdateOrInsertRange(idUser, dtos, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт РТК из excel (xlsx) файла
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="files"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("parse")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
public async Task<ActionResult<ParserResultDto<TDto>>> Parse(int idWell,
|
||||
[FromForm] IFormFileCollection files,
|
||||
CancellationToken token)
|
||||
{
|
||||
await AssertUserHasAccessToWell(idWell, token);
|
||||
|
||||
var stream = files.GetExcelFile();
|
||||
|
||||
try
|
||||
{
|
||||
var dto = parserService.Parse(stream, IParserOptionsRequest.Empty());
|
||||
return Ok(dto);
|
||||
}
|
||||
catch (FileFormatException ex)
|
||||
{
|
||||
return this.ValidationBadRequest(nameof(files), ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение шаблона для заполнения РТК
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("template")]
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
public IActionResult GetTemplate()
|
||||
{
|
||||
var stream = parserService.GetTemplateFile();
|
||||
return File(stream, "application/octet-stream", TemplateFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns user id, if he has access to well
|
||||
@ -221,4 +274,4 @@ public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase
|
||||
var idUser = User.GetUserId() ?? throw new ForbidException("Неизвестный пользователь");
|
||||
return idUser;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,15 +2,18 @@
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
|
||||
|
||||
namespace AsbCloudWebApi.Controllers.ProcessMapPlan;
|
||||
|
||||
public class ProcessMapPlanDrillingController : ProcessMapPlanBaseController<ProcessMapPlanDrillingDto>
|
||||
{
|
||||
public ProcessMapPlanDrillingController(
|
||||
IChangeLogRepository<ProcessMapPlanDrillingDto, ProcessMapPlanBaseRequestWithWell> repository,
|
||||
IWellService wellService)
|
||||
: base(repository, wellService)
|
||||
{
|
||||
}
|
||||
}
|
||||
public ProcessMapPlanDrillingController(IChangeLogRepository<ProcessMapPlanDrillingDto, ProcessMapPlanBaseRequestWithWell> repository,
|
||||
IWellService wellService,
|
||||
ProcessMapPlanDrillingParser parserService)
|
||||
: base(repository, wellService, parserService)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string TemplateFileName => "ЕЦП_шаблон_файла_РТК_план_бурение.xlsx";
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using System;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Export;
|
||||
@ -11,8 +12,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
using AsbCloudInfrastructure.Services;
|
||||
using AsbCloudWebApi.Controllers.Interfaces;
|
||||
using AsbCloudInfrastructure.Services.Parser;
|
||||
|
||||
namespace AsbCloudWebApi.Controllers.Trajectory
|
||||
{
|
||||
@ -22,42 +22,23 @@ namespace AsbCloudWebApi.Controllers.Trajectory
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
public abstract class TrajectoryEditableController<TDto> : TrajectoryController<TDto>,
|
||||
IControllerWithParser<TDto, IParserOptionsRequest>
|
||||
where TDto : TrajectoryGeoDto
|
||||
public abstract class TrajectoryEditableController<TDto> : TrajectoryController<TDto>
|
||||
where TDto : TrajectoryGeoDto
|
||||
{
|
||||
private readonly IParserService<TDto, IParserOptionsRequest> parserService;
|
||||
private readonly ParserExcelService<TDto> parserService;
|
||||
private readonly ITrajectoryEditableRepository<TDto> trajectoryRepository;
|
||||
|
||||
protected TrajectoryEditableController(IWellService wellService,
|
||||
ParserServiceFactory parserServiceFactory,
|
||||
ParserExcelService<TDto> parserService,
|
||||
TrajectoryExportService<TDto> trajectoryExportService,
|
||||
ITrajectoryEditableRepository<TDto> trajectoryRepository,
|
||||
int idParserService)
|
||||
: base(
|
||||
wellService,
|
||||
trajectoryExportService,
|
||||
trajectoryRepository)
|
||||
{
|
||||
parserService = parserServiceFactory.Create<TDto, IParserOptionsRequest>(idParserService);
|
||||
ITrajectoryEditableRepository<TDto> trajectoryRepository)
|
||||
: base(wellService, trajectoryExportService, trajectoryRepository)
|
||||
{
|
||||
this.parserService = parserService;
|
||||
this.trajectoryRepository = trajectoryRepository;
|
||||
}
|
||||
|
||||
ActionResult<ParserResultDto<TDto>> IControllerWithParser<TDto, IParserOptionsRequest>.Parse(Stream file,
|
||||
IParserOptionsRequest options)
|
||||
{
|
||||
try
|
||||
{
|
||||
var parserResult = parserService.Parse(file, options);
|
||||
return Ok(parserResult);
|
||||
}
|
||||
catch (FileFormatException ex)
|
||||
{
|
||||
return this.ValidationBadRequest("files", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Возвращает excel шаблон для заполнения строк траектории
|
||||
/// </summary>
|
||||
/// <returns>Запрашиваемый файл</returns>
|
||||
@ -93,7 +74,17 @@ namespace AsbCloudWebApi.Controllers.Trajectory
|
||||
if (!await CanUserAccessToWellAsync(idWell, token))
|
||||
return Forbid();
|
||||
|
||||
return this.ParseExcelFile(files, IParserOptionsRequest.Empty());
|
||||
var stream = files.GetExcelFile();
|
||||
|
||||
try
|
||||
{
|
||||
var dto = parserService.Parse(stream, IParserOptionsRequest.Empty());
|
||||
return Ok(dto);
|
||||
}
|
||||
catch (FileFormatException ex)
|
||||
{
|
||||
return this.ValidationBadRequest(nameof(files), ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,8 +1,8 @@
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudInfrastructure.Services;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Export;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AsbCloudWebApi.Controllers.Trajectory;
|
||||
@ -18,13 +18,9 @@ public class TrajectoryFactManualController : TrajectoryEditableController<Traje
|
||||
|
||||
public TrajectoryFactManualController(IWellService wellService,
|
||||
TrajectoryFactManualExportService trajectoryExportService,
|
||||
ParserServiceFactory parserServiceFactory,
|
||||
TrajectoryFactManualParser parserService,
|
||||
ITrajectoryEditableRepository<TrajectoryGeoFactDto> trajectoryRepository)
|
||||
: base(wellService,
|
||||
parserServiceFactory,
|
||||
trajectoryExportService,
|
||||
trajectoryRepository,
|
||||
ParserServiceFactory.IdTrajectoryFactManualParserService)
|
||||
: base(wellService, parserService, trajectoryExportService, trajectoryRepository)
|
||||
{
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudInfrastructure.Services;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
namespace AsbCloudWebApi.Controllers.Trajectory
|
||||
{
|
||||
@ -23,15 +23,11 @@ namespace AsbCloudWebApi.Controllers.Trajectory
|
||||
protected override string fileName => "ЕЦП_шаблон_файла_плановая_траектория.xlsx";
|
||||
|
||||
public TrajectoryPlanController(IWellService wellService,
|
||||
TrajectoryPlanParser parserService,
|
||||
TrajectoryPlanExportService trajectoryExportService,
|
||||
ParserServiceFactory parserServiceFactory,
|
||||
ITrajectoryEditableRepository<TrajectoryGeoPlanDto> trajectoryRepository,
|
||||
TrajectoryService trajectoryVisualizationService)
|
||||
: base(wellService,
|
||||
parserServiceFactory,
|
||||
trajectoryExportService,
|
||||
trajectoryRepository,
|
||||
ParserServiceFactory.IdTrajectoryPlanParserService)
|
||||
: base(wellService, parserService, trajectoryExportService, trajectoryRepository)
|
||||
{
|
||||
this.trajectoryVisualizationService = trajectoryVisualizationService;
|
||||
}
|
||||
|
@ -8,8 +8,9 @@ using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Exceptions;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
using AsbCloudWebApi.Controllers.Interfaces;
|
||||
using AsbCloudInfrastructure.Services.Parser;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc;
|
||||
@ -96,30 +97,20 @@ public static class Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызов парсера со стандартной валидацией входного файла
|
||||
/// Получение Excel
|
||||
/// </summary>
|
||||
/// <typeparam name="TDto"></typeparam>
|
||||
/// <typeparam name="TOptions"></typeparam>
|
||||
/// <param name="controller"></param>
|
||||
/// <param name="files"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static ActionResult<ParserResultDto<TDto>> ParseExcelFile<TDto, TOptions>(
|
||||
this IControllerWithParser<TDto, TOptions> controller,
|
||||
IFormFileCollection files,
|
||||
TOptions options)
|
||||
where TDto : class, IId
|
||||
where TOptions : class, IParserOptionsRequest
|
||||
/// <exception cref="ArgumentInvalidException"></exception>
|
||||
public static Stream GetExcelFile(this IFormFileCollection files)
|
||||
{
|
||||
if (files.Count < 1)
|
||||
return MakeBadRequestObjectResult(nameof(files), "Нет файла");
|
||||
throw new ArgumentInvalidException(nameof(files), "Нет файла");
|
||||
|
||||
var file = files[0];
|
||||
if (Path.GetExtension(file.FileName).ToLower() != ".xlsx")
|
||||
return MakeBadRequestObjectResult(nameof(files), "Требуется .xlsx файл.");
|
||||
throw new ArgumentInvalidException(nameof(files), "Требуется .xlsx файл.");
|
||||
|
||||
var stream = file.OpenReadStream();
|
||||
|
||||
return controller.Parse(stream, options);
|
||||
return file.OpenReadStream();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user