forked from ddrilling/AsbCloudServer
Фикс часовых поясов + покрытие этого бага тестами
This commit is contained in:
parent
3b7616ba43
commit
20172a886b
@ -5,20 +5,24 @@ using System.Linq;
|
|||||||
using AsbCloudApp.Data;
|
using AsbCloudApp.Data;
|
||||||
using AsbCloudApp.Data.WellOperationImport;
|
using AsbCloudApp.Data.WellOperationImport;
|
||||||
using AsbCloudApp.Repositories;
|
using AsbCloudApp.Repositories;
|
||||||
|
using AsbCloudApp.Services;
|
||||||
using AsbCloudApp.Services.WellOperationImport;
|
using AsbCloudApp.Services.WellOperationImport;
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Services.WellOperationImport;
|
namespace AsbCloudInfrastructure.Services.WellOperationImport;
|
||||||
|
|
||||||
public class WellOperationImportService : IWellOperationImportService
|
public class WellOperationImportService : IWellOperationImportService
|
||||||
{
|
{
|
||||||
|
private readonly IWellService wellService;
|
||||||
private readonly IWellOperationRepository wellOperationRepository;
|
private readonly IWellOperationRepository wellOperationRepository;
|
||||||
|
|
||||||
private static readonly DateTime dateLimitMin = new(2001, 1, 1, 0, 0, 0);
|
private static readonly DateTime dateLimitMin = new(2001, 1, 1, 0, 0, 0);
|
||||||
private static readonly DateTime dateLimitMax = new(2099, 1, 1, 0, 0, 0);
|
private static readonly DateTime dateLimitMax = new(2099, 1, 1, 0, 0, 0);
|
||||||
private static readonly TimeSpan drillingDurationLimitMax = TimeSpan.FromDays(366);
|
private static readonly TimeSpan drillingDurationLimitMax = TimeSpan.FromDays(366);
|
||||||
|
|
||||||
public WellOperationImportService(IWellOperationRepository wellOperationRepository)
|
public WellOperationImportService(IWellService wellService,
|
||||||
|
IWellOperationRepository wellOperationRepository)
|
||||||
{
|
{
|
||||||
|
this.wellService = wellService;
|
||||||
this.wellOperationRepository = wellOperationRepository;
|
this.wellOperationRepository = wellOperationRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +35,11 @@ public class WellOperationImportService : IWellOperationImportService
|
|||||||
|
|
||||||
var wellOperations = new List<WellOperationDto>();
|
var wellOperations = new List<WellOperationDto>();
|
||||||
|
|
||||||
foreach (var row in sheet.Rows)
|
var rows = sheet.Rows.OrderBy(r => r.Date);
|
||||||
|
|
||||||
|
var prevRow = new RowDto();
|
||||||
|
|
||||||
|
foreach (var row in rows)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -59,14 +67,15 @@ public class WellOperationImportService : IWellOperationImportService
|
|||||||
throw new FileFormatException(
|
throw new FileFormatException(
|
||||||
$"Лист '{sheet.Name}'. Строка '{row.Number}' неправильно получена дата начала операции");
|
$"Лист '{sheet.Name}'. Строка '{row.Number}' неправильно получена дата начала операции");
|
||||||
|
|
||||||
if (wellOperations.LastOrDefault()?.DateStart > row.Date)
|
if (prevRow.Date > row.Date)
|
||||||
throw new FileFormatException(
|
throw new FileFormatException(
|
||||||
$"Лист '{sheet.Name}' строка '{row.Number}' дата позднее даты предыдущей операции");
|
$"Лист '{sheet.Name}' строка '{row.Number}' дата позднее даты предыдущей операции");
|
||||||
|
|
||||||
if (row.Duration is not (>= 0d and <= 240d))
|
if (row.Duration is not (>= 0d and <= 240d))
|
||||||
throw new FileFormatException($"Лист '{sheet.Name}'. Строка '{row.Number}' некорректная длительность операции");
|
throw new FileFormatException($"Лист '{sheet.Name}'. Строка '{row.Number}' некорректная длительность операции");
|
||||||
|
|
||||||
wellOperations.Add(new WellOperationDto
|
var timezone = wellService.GetTimezone(idWell);
|
||||||
|
var wellOperation = new WellOperationDto
|
||||||
{
|
{
|
||||||
IdWell = idWell,
|
IdWell = idWell,
|
||||||
IdUser = idUser,
|
IdUser = idUser,
|
||||||
@ -76,10 +85,14 @@ public class WellOperationImportService : IWellOperationImportService
|
|||||||
CategoryInfo = row.CategoryInfo,
|
CategoryInfo = row.CategoryInfo,
|
||||||
DepthStart = row.DepthStart,
|
DepthStart = row.DepthStart,
|
||||||
DepthEnd = row.DepthEnd,
|
DepthEnd = row.DepthEnd,
|
||||||
DateStart = row.Date,
|
DateStart = row.Date.ToUtcDateTimeOffset(timezone.Hours).ToRemoteDateTime(timezone.Hours),
|
||||||
DurationHours = row.Duration,
|
DurationHours = row.Duration,
|
||||||
Comment = row.Comment
|
Comment = row.Comment
|
||||||
});
|
};
|
||||||
|
|
||||||
|
wellOperations.Add(wellOperation);
|
||||||
|
|
||||||
|
prevRow = row;
|
||||||
}
|
}
|
||||||
catch (FileFormatException ex)
|
catch (FileFormatException ex)
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,14 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="WellOperationsPlan.xlsx" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="WellOperationsPlan.xlsx" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AsbCloudWebApi\AsbCloudWebApi.csproj" />
|
<ProjectReference Include="..\AsbCloudWebApi\AsbCloudWebApi.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -6,16 +6,22 @@ namespace AsbCloudWebApi.IntegrationTests.Clients;
|
|||||||
|
|
||||||
public interface IWellOperationClient
|
public interface IWellOperationClient
|
||||||
{
|
{
|
||||||
private const string BaseRoute = "/api/well/{idWell}/wellOperations";
|
private const string BaseRoute = "/api/well/{idWell}/wellOperations";
|
||||||
|
|
||||||
[Post(BaseRoute + "/{idType}/{deleteBeforeInsert}")]
|
[Post(BaseRoute + "/{idType}/{deleteBeforeInsert}")]
|
||||||
Task<IApiResponse<int>> InsertRangeAsync(int idWell, int idType, bool deleteBeforeInsert, [Body] IEnumerable<WellOperationDto> dtos);
|
Task<IApiResponse<int>> InsertRangeAsync(int idWell, int idType, bool deleteBeforeInsert, [Body] IEnumerable<WellOperationDto> dtos);
|
||||||
|
|
||||||
[Put(BaseRoute + "/{idOperation}")]
|
[Put(BaseRoute + "/{idOperation}")]
|
||||||
Task<IApiResponse<int>> UpdateAsync(int idWell, int idOperation, [Body] WellOperationDto value, CancellationToken token);
|
Task<IApiResponse<int>> UpdateAsync(int idWell, int idOperation, [Body] WellOperationDto value, CancellationToken token);
|
||||||
|
|
||||||
[Get(BaseRoute + "/plan")]
|
[Get(BaseRoute + "/plan")]
|
||||||
Task<IApiResponse<PaginationContainer<WellOperationDto>>> GetPageOperationsPlanAsync(int idWell,
|
Task<IApiResponse<PaginationContainer<WellOperationDto>>> GetPageOperationsPlanAsync(int idWell,
|
||||||
[Query] WellOperationRequestBase request,
|
[Query] WellOperationRequestBase request,
|
||||||
CancellationToken token);
|
CancellationToken token);
|
||||||
|
|
||||||
|
[Multipart]
|
||||||
|
[Post(BaseRoute + "/import/plan/default")]
|
||||||
|
Task<IApiResponse<IEnumerable<WellOperationDto>>> ImportPlanDefaultExcelFileAsync(int idWell,
|
||||||
|
[AliasAs("files")] IEnumerable<StreamPart> streams,
|
||||||
|
CancellationToken token);
|
||||||
}
|
}
|
@ -2,7 +2,9 @@ using AsbCloudApp.Data;
|
|||||||
using AsbCloudDb.Model;
|
using AsbCloudDb.Model;
|
||||||
using AsbCloudWebApi.IntegrationTests.Clients;
|
using AsbCloudWebApi.IntegrationTests.Clients;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
using AsbCloudApp.Requests;
|
using AsbCloudApp.Requests;
|
||||||
|
using Refit;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace AsbCloudWebApi.IntegrationTests.Controllers;
|
namespace AsbCloudWebApi.IntegrationTests.Controllers;
|
||||||
@ -211,4 +213,39 @@ public class WellOperationControllerTest : BaseIntegrationTest
|
|||||||
var excludeProps = new[] { nameof(WellOperationDto.Id) };
|
var excludeProps = new[] { nameof(WellOperationDto.Id) };
|
||||||
MatchHelper.Match(dto, wellOperation, excludeProps);
|
MatchHelper.Match(dto, wellOperation, excludeProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ImportPlanDefaultExcelFileAsync_returns_success()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
const int timeZoneUtc = 5;
|
||||||
|
|
||||||
|
//TODO: вынести в метод расширения. Сделать когда доберёмся до рефакторинга операций по скважине
|
||||||
|
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();
|
||||||
|
stream.CopyTo(memoryStream);
|
||||||
|
memoryStream.Position = 0;
|
||||||
|
|
||||||
|
//act
|
||||||
|
var streamPart = new StreamPart(memoryStream, "WellOperations.xlsx", "application/octet-stream");
|
||||||
|
|
||||||
|
var response = await client.ImportPlanDefaultExcelFileAsync(idWell, new[] { streamPart }, CancellationToken.None);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.Equal(4, response.Content.Count());
|
||||||
|
Assert.True(response.Content.All(w => Math.Abs(w.DateStart.Offset.Hours - timeZoneUtc) < 0.1));
|
||||||
|
}
|
||||||
}
|
}
|
BIN
AsbCloudWebApi.IntegrationTests/WellOperationsPlan.xlsx
Normal file
BIN
AsbCloudWebApi.IntegrationTests/WellOperationsPlan.xlsx
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user