diff --git a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
index 00349a6c..c6edb30b 100644
--- a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
+++ b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
@@ -60,16 +60,15 @@ public class WellOperationRepository : IWellOperationRepository
OperationType = WellOperation.IdOperationTypePlan,
};
- var entities = await BuildQuery(request)
+ var dtos = await BuildQuery(request)
.AsNoTracking()
- .ToArrayAsync(token)
- .ConfigureAwait(false);
+ .ToArrayAsync(token);
var dateLastAssosiatedPlanOperation = await GetDateLastAssosiatedPlanOperationAsync(idWell, currentDate, timezone.Hours, token);
-
+
var result = new WellOperationPlanDto()
{
- WellOperationsPlan = entities,
+ WellOperationsPlan = dtos.Select(Convert),
DateLastAssosiatedPlanOperation = dateLastAssosiatedPlanOperation
};
@@ -211,7 +210,7 @@ public class WellOperationRepository : IWellOperationRepository
var dtos = await query.ToArrayAsync(token);
- return dtos;
+ return dtos.Select(Convert);
}
///
@@ -219,21 +218,19 @@ public class WellOperationRepository : IWellOperationRepository
WellOperationRequest request,
CancellationToken token)
{
- var query = BuildQuery(request)
- .AsNoTracking();
-
+ var query = BuildQuery(request);
+
var result = new PaginationContainer
{
Skip = request.Skip ?? 0,
Take = request.Take ?? 32,
- Count = await query.CountAsync(token).ConfigureAwait(false),
+ Count = await query.CountAsync(token),
};
- query = query
- .Skip(result.Skip)
- .Take(result.Take);
+ var dtos = await query.ToArrayAsync(token);
+
+ result.Items = dtos.Select(Convert);
- result.Items = await query.ToArrayAsync(token);
return result;
}
@@ -384,12 +381,13 @@ public class WellOperationRepository : IWellOperationRepository
/// В результате попрежнему требуется конвертировать дату
///
///
+ ///
///
private IQueryable BuildQuery(WellOperationRequest request)
{
var timezone = wellService.GetTimezone(request.IdWell);
- var timeZoneOffset = TimeSpan.FromHours(timezone.Hours);
-
+ var timeZoneOffset = timezone.Hours;
+
var query = db.WellOperations
.Include(s => s.WellSectionType)
.Include(s => s.OperationCategory)
@@ -413,20 +411,20 @@ public class WellOperationRepository : IWellOperationRepository
if (request.GeDate.HasValue)
{
- var geDateOffset = request.GeDate.Value.ToUtcDateTimeOffset(timezone.Hours);
+ var geDateOffset = request.GeDate.Value.ToUtcDateTimeOffset(timeZoneOffset);
query = query.Where(e => e.DateStart >= geDateOffset);
}
if (request.LtDate.HasValue)
{
- var ltDateOffset = request.LtDate.Value.ToUtcDateTimeOffset(timezone.Hours);
+ var ltDateOffset = request.LtDate.Value.ToUtcDateTimeOffset(timeZoneOffset);
query = query.Where(e => e.DateStart < ltDateOffset);
}
var currentWellOperations = db.WellOperations
.Where(subOp => subOp.IdWell == request.IdWell);
- var wellOperationsWithCategoryNPT = currentWellOperations
+ var wellOperationsWithCategoryNpt = currentWellOperations
.Where(subOp => subOp.IdType == 1)
.Where(subOp => WellOperationCategory.NonProductiveTimeSubIds.Contains(subOp.IdCategory));
@@ -442,14 +440,14 @@ public class WellOperationRepository : IWellOperationRepository
CategoryName = o.OperationCategory.Name,
WellSectionTypeName = o.WellSectionType.Caption,
- DateStart = DateTime.SpecifyKind(o.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified),
+ DateStart = o.DateStart,
DepthStart = o.DepthStart,
DepthEnd = o.DepthEnd,
DurationHours = o.DurationHours,
CategoryInfo = o.CategoryInfo,
Comment = o.Comment,
- NptHours = wellOperationsWithCategoryNPT
+ NptHours = wellOperationsWithCategoryNpt
.Where(subOp => subOp.DateStart <= o.DateStart)
.Select(subOp => subOp.DurationHours)
.Sum(),
@@ -460,22 +458,39 @@ public class WellOperationRepository : IWellOperationRepository
.Min(subOp => subOp.DateStart))
.TotalDays,
IdUser = o.IdUser,
- LastUpdateDate = DateTime.SpecifyKind(o.LastUpdateDate.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified)
+ LastUpdateDate = o.LastUpdateDate,
});
if (request.SortFields?.Any() == true)
{
dtos = dtos.SortBy(request.SortFields);
}
- else
- {
- dtos = dtos
- .OrderBy(e => e.DateStart)
- .ThenBy(e => e.DepthEnd)
- .ThenBy(e => e.Id);
- }
- return dtos;
+ dtos = dtos
+ .OrderBy(e => e.DateStart)
+ .ThenBy(e => e.DepthEnd)
+ .ThenBy(e => e.Id);
+
+ if (request.Skip.HasValue)
+ dtos = dtos.Skip(request.Skip.Value);
+
+ if (request.Take.HasValue)
+ dtos = dtos.Take(request.Take.Value);
+
+ return dtos.AsNoTracking();
+ }
+
+ private WellOperationDto Convert(WellOperationDto dto)
+ {
+ var timezone = wellService.GetTimezone(dto.IdWell);
+ var timezoneOffset = TimeSpan.FromHours(timezone.Hours);
+
+ var dtoWithRemoteDateTime = dto.Adapt();
+
+ dtoWithRemoteDateTime.DateStart = dto.DateStart.ToOffset(TimeSpan.FromHours(timezoneOffset.Hours));
+ dtoWithRemoteDateTime.LastUpdateDate = dto.LastUpdateDate?.ToOffset(TimeSpan.FromHours(timezoneOffset.Hours));
+
+ return dtoWithRemoteDateTime;
}
public async Task RemoveDuplicates(Action onProgressCallback, CancellationToken token)
diff --git a/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs b/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs
index b8042275..800979e5 100644
--- a/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs
+++ b/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs
@@ -5,20 +5,24 @@ using System.Linq;
using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperationImport;
using AsbCloudApp.Repositories;
+using AsbCloudApp.Services;
using AsbCloudApp.Services.WellOperationImport;
namespace AsbCloudInfrastructure.Services.WellOperationImport;
public class WellOperationImportService : IWellOperationImportService
{
+ private readonly IWellService wellService;
private readonly IWellOperationRepository wellOperationRepository;
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 TimeSpan drillingDurationLimitMax = TimeSpan.FromDays(366);
- public WellOperationImportService(IWellOperationRepository wellOperationRepository)
+ public WellOperationImportService(IWellService wellService,
+ IWellOperationRepository wellOperationRepository)
{
+ this.wellService = wellService;
this.wellOperationRepository = wellOperationRepository;
}
@@ -30,8 +34,12 @@ public class WellOperationImportService : IWellOperationImportService
var categories = wellOperationRepository.GetCategories(false);
var wellOperations = new List();
-
- foreach (var row in sheet.Rows)
+
+ var rows = sheet.Rows.OrderBy(r => r.Date);
+
+ var prevRow = new RowDto();
+
+ foreach (var row in rows)
{
try
{
@@ -58,15 +66,18 @@ public class WellOperationImportService : IWellOperationImportService
if (row.Date < dateLimitMin && row.Date > dateLimitMax)
throw new FileFormatException(
$"Лист '{sheet.Name}'. Строка '{row.Number}' неправильно получена дата начала операции");
-
- if (wellOperations.LastOrDefault()?.DateStart > row.Date)
+
+ if (prevRow.Date > row.Date)
throw new FileFormatException(
$"Лист '{sheet.Name}' строка '{row.Number}' дата позднее даты предыдущей операции");
if (row.Duration is not (>= 0d and <= 240d))
throw new FileFormatException($"Лист '{sheet.Name}'. Строка '{row.Number}' некорректная длительность операции");
- wellOperations.Add(new WellOperationDto
+ var timezone = wellService.GetTimezone(idWell);
+ var timezoneOffset = TimeSpan.FromHours(timezone.Hours);
+
+ var wellOperation = new WellOperationDto
{
IdWell = idWell,
IdUser = idUser,
@@ -76,10 +87,14 @@ public class WellOperationImportService : IWellOperationImportService
CategoryInfo = row.CategoryInfo,
DepthStart = row.DepthStart,
DepthEnd = row.DepthEnd,
- DateStart = row.Date,
+ DateStart = new DateTimeOffset(row.Date, timezoneOffset),
DurationHours = row.Duration,
Comment = row.Comment
- });
+ };
+
+ wellOperations.Add(wellOperation);
+
+ prevRow = row;
}
catch (FileFormatException ex)
{
diff --git a/AsbCloudWebApi.IntegrationTests/AsbCloudWebApi.IntegrationTests.csproj b/AsbCloudWebApi.IntegrationTests/AsbCloudWebApi.IntegrationTests.csproj
index a9d7713a..1c641f69 100644
--- a/AsbCloudWebApi.IntegrationTests/AsbCloudWebApi.IntegrationTests.csproj
+++ b/AsbCloudWebApi.IntegrationTests/AsbCloudWebApi.IntegrationTests.csproj
@@ -18,6 +18,14 @@
+
+
+
+
+
+
+
+
diff --git a/AsbCloudWebApi.IntegrationTests/Clients/IWellOperationClient.cs b/AsbCloudWebApi.IntegrationTests/Clients/IWellOperationClient.cs
index 07754ed1..8e2db98f 100644
--- a/AsbCloudWebApi.IntegrationTests/Clients/IWellOperationClient.cs
+++ b/AsbCloudWebApi.IntegrationTests/Clients/IWellOperationClient.cs
@@ -6,16 +6,22 @@ namespace AsbCloudWebApi.IntegrationTests.Clients;
public interface IWellOperationClient
{
- private const string BaseRoute = "/api/well/{idWell}/wellOperations";
+ private const string BaseRoute = "/api/well/{idWell}/wellOperations";
- [Post(BaseRoute + "/{idType}/{deleteBeforeInsert}")]
- Task> InsertRangeAsync(int idWell, int idType, bool deleteBeforeInsert, [Body] IEnumerable dtos);
+ [Post(BaseRoute + "/{idType}/{deleteBeforeInsert}")]
+ Task> InsertRangeAsync(int idWell, int idType, bool deleteBeforeInsert, [Body] IEnumerable dtos);
- [Put(BaseRoute + "/{idOperation}")]
- Task> UpdateAsync(int idWell, int idOperation, [Body] WellOperationDto value, CancellationToken token);
+ [Put(BaseRoute + "/{idOperation}")]
+ Task> UpdateAsync(int idWell, int idOperation, [Body] WellOperationDto value, CancellationToken token);
[Get(BaseRoute + "/plan")]
Task>> GetPageOperationsPlanAsync(int idWell,
[Query] WellOperationRequestBase request,
CancellationToken token);
+
+ [Multipart]
+ [Post(BaseRoute + "/import/plan/default")]
+ Task>> ImportPlanDefaultExcelFileAsync(int idWell,
+ [AliasAs("files")] IEnumerable streams,
+ CancellationToken token);
}
\ No newline at end of file
diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperationControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperationControllerTest.cs
index 46ce1d4d..a47d5f09 100644
--- a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperationControllerTest.cs
+++ b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperationControllerTest.cs
@@ -2,7 +2,10 @@ using AsbCloudApp.Data;
using AsbCloudDb.Model;
using AsbCloudWebApi.IntegrationTests.Clients;
using System.Net;
+using System.Reflection;
using AsbCloudApp.Requests;
+using AsbCloudWebApi.IntegrationTests.Data;
+using Refit;
using Xunit;
namespace AsbCloudWebApi.IntegrationTests.Controllers;
@@ -28,7 +31,7 @@ public class WellOperationControllerTest : BaseIntegrationTest
DepthEnd = 20.0,
Day = 0.0,
NptHours = 0.0,
- DateStart = new DateTimeOffset(new DateTime(2023, 02, 03, 1, 0, 0, DateTimeKind.Unspecified)),
+ DateStart = new DateTimeOffset(new DateTime(2023, 1, 10), TimeSpan.FromHours(Defaults.Wells[0].Timezone.Hours)),
DurationHours = 1.0,
Comment = "1",
IdUser = 1,
@@ -117,4 +120,37 @@ public class WellOperationControllerTest : BaseIntegrationTest
var excludeProps = new[] { nameof(WellOperationDto.Id) };
MatchHelper.Match(dto, wellOperation, excludeProps);
}
+
+ [Fact]
+ public async Task ImportPlanDefaultExcelFileAsync_returns_success()
+ {
+ //arrange
+ //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 - Defaults.Wells[0].Timezone.Hours) < 0.1));
+ }
}
\ No newline at end of file
diff --git a/AsbCloudWebApi.IntegrationTests/WellOperationsPlan.xlsx b/AsbCloudWebApi.IntegrationTests/WellOperationsPlan.xlsx
new file mode 100644
index 00000000..dd1a2781
Binary files /dev/null and b/AsbCloudWebApi.IntegrationTests/WellOperationsPlan.xlsx differ
diff --git a/AsbCloudWebApi.Tests/Services/WellOperationExport/WellOperationExportServiceTest.cs b/AsbCloudWebApi.Tests/Services/WellOperationExport/WellOperationExportServiceTest.cs
index 2590df26..3d66aab9 100644
--- a/AsbCloudWebApi.Tests/Services/WellOperationExport/WellOperationExportServiceTest.cs
+++ b/AsbCloudWebApi.Tests/Services/WellOperationExport/WellOperationExportServiceTest.cs
@@ -117,7 +117,7 @@ namespace AsbCloudWebApi.Tests.Services.WellOperationExport
wellOperationImportTemplateService = new WellOperationImportTemplateService();
wellOperationExportService = new WellOperationExportService(wellOperationRepository, wellService, wellOperationImportTemplateService);
- wellOperationImportService = new WellOperationImportService(wellOperationRepository);
+ wellOperationImportService = new WellOperationImportService(wellService, wellOperationRepository);
wellOperationDefaultExcelParser = new WellOperationDefaultExcelParser();
this.output = output;
}
diff --git a/AsbCloudWebApi/Controllers/WellOperationController.cs b/AsbCloudWebApi/Controllers/WellOperationController.cs
index 83f2987a..0d53a4ad 100644
--- a/AsbCloudWebApi/Controllers/WellOperationController.cs
+++ b/AsbCloudWebApi/Controllers/WellOperationController.cs
@@ -281,7 +281,6 @@ namespace AsbCloudWebApi.Controllers
foreach (var wellOperation in wellOperations)
{
wellOperation.IdWell = idWell;
- wellOperation.LastUpdateDate = DateTimeOffset.UtcNow;
wellOperation.IdUser = User.GetUserId();
wellOperation.IdType = idType;
}
@@ -313,7 +312,6 @@ namespace AsbCloudWebApi.Controllers
value.IdWell = idWell;
value.Id = idOperation;
- value.LastUpdateDate = DateTimeOffset.UtcNow;
value.IdUser = User.GetUserId();
var result = await operationRepository.UpdateAsync(value, token)