diff --git a/AsbCloudApp/Services/IDetectedOperationService.cs b/AsbCloudApp/Services/IDetectedOperationService.cs
index 9ab9189e..262b193f 100644
--- a/AsbCloudApp/Services/IDetectedOperationService.cs
+++ b/AsbCloudApp/Services/IDetectedOperationService.cs
@@ -72,6 +72,7 @@ namespace AsbCloudApp.Services
///
///
///
+ [Obsolete]
Task> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token);
///
diff --git a/AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs b/AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs
index 944dabd5..80bc3a99 100644
--- a/AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs
+++ b/AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs
@@ -133,18 +133,22 @@ namespace AsbCloudInfrastructure.Repository
if (ids.Length != dtos.Count())
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь уникальные Id");
- var dbSet = dbContext.Set();
-
- var existingEntitiesCount = await dbSet
+ var existingEntitiesCount = await dbContext.Set()
.Where(o => ids.Contains(o.Id))
.CountAsync(token);
if (ids.Length != existingEntitiesCount)
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны существовать в БД");
+
+ var entities = dtos.Select(Convert);
- var entities = dbContext.Set().Where(e => ids.Contains(e.Id));
- dbContext.Set().UpdateRange(entities);
- return await dbContext.SaveChangesAsync(token);
+ var entries = entities.Select(entity => dbContext.Set().Update(entity)).ToList();
+
+ var affected = await dbContext.SaveChangesAsync(token);
+
+ entries.ForEach(entry => entry.State = EntityState.Detached);
+
+ return affected;
}
///
diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
index 167dc4de..0cac62b2 100644
--- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
+++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
@@ -143,6 +143,7 @@ public class DetectedOperationService : IDetectedOperationService
}
}
+ [Obsolete]
public async Task> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
diff --git a/AsbCloudInfrastructure/Services/SAUB/SetpointsService.cs b/AsbCloudInfrastructure/Services/SAUB/SetpointsService.cs
index e68eeef3..d91c893d 100644
--- a/AsbCloudInfrastructure/Services/SAUB/SetpointsService.cs
+++ b/AsbCloudInfrastructure/Services/SAUB/SetpointsService.cs
@@ -96,7 +96,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
return 0;
entity.IdState = setpointsRequestDto.IdState;
- var affected = await setpointsRepository.UpdateAsync(entity, token);
+ var affected = await setpointsRepository.UpdateRangeAsync(new[] { entity }, token);
return affected;
}
diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs
index 795d944b..a10ff824 100644
--- a/AsbCloudInfrastructure/Services/WellService.cs
+++ b/AsbCloudInfrastructure/Services/WellService.cs
@@ -274,6 +274,7 @@ namespace AsbCloudInfrastructure.Services
dto.Timezone = GetTimezone(entity.Id);
dto.StartDate = dbContext.WellOperations.Where(e => e.IdType == WellOperation.IdOperationTypeFact)
+ .AsNoTracking()
.MinOrDefault(e => e.DateStart)?.ToRemoteDateTime(dto.Timezone.Hours);
dto.WellType = entity.WellType.Caption;
dto.Cluster = entity.Cluster.Caption;
diff --git a/AsbCloudWebApi.IntegrationTests/Clients/IWellOperationClient.cs b/AsbCloudWebApi.IntegrationTests/Clients/IWellOperationClient.cs
index b9e63518..9a9a5b7e 100644
--- a/AsbCloudWebApi.IntegrationTests/Clients/IWellOperationClient.cs
+++ b/AsbCloudWebApi.IntegrationTests/Clients/IWellOperationClient.cs
@@ -29,4 +29,10 @@ public interface IWellOperationClient
[Get(BaseRoute + "/export")]
Task> ExportAsync(int idWell, int idType);
+
+ [Get(BaseRoute + "/template")]
+ Task> GetTemplate(int idWell, int idType);
+
+ [Get(BaseRoute + "/categories")]
+ Task>> GetCategories(int idWell, bool includeParents, bool includeHidden);
}
\ No newline at end of file
diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/WellOperationControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/WellOperationControllerTest.cs
index 13ed2fbc..4f44333e 100644
--- a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/WellOperationControllerTest.cs
+++ b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperations/WellOperationControllerTest.cs
@@ -1,193 +1,252 @@
-using System.Net;
-using System.Reflection;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Requests;
using AsbCloudDb.Model;
+using AsbCloudInfrastructure;
using AsbCloudWebApi.IntegrationTests.Clients;
using AsbCloudWebApi.IntegrationTests.Data;
+using ClosedXML.Excel;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Refit;
+using System.Net;
+using System.Reflection;
using Xunit;
namespace AsbCloudWebApi.IntegrationTests.Controllers.WellOperations;
public class WellOperationControllerTest : BaseIntegrationTest
{
- private IWellOperationClient client;
+ private IWellOperationClient client;
- public WellOperationControllerTest(WebAppFactoryFixture factory)
- : base(factory)
- {
- client = factory.GetAuthorizedHttpClient(string.Empty);
+ public WellOperationControllerTest(WebAppFactoryFixture factory)
+ : base(factory)
+ {
+ client = factory.GetAuthorizedHttpClient(string.Empty);
- dbContext.CleanupDbSet();
- }
+ dbContext.CleanupDbSet();
+ }
- ///
- /// Успешное добавление операций (без предварительной очистки данных)
- ///
- ///
- [Fact]
- public async Task InsertRange_returns_success()
- {
- //arrange
- var well = await dbContext.Wells.FirstAsync();
- var entity = CreateWellOperation(well.Id);
- var dtos = new[] { entity.Adapt() };
+ ///
+ /// Успешное добавление операций (без предварительной очистки данных)
+ ///
+ ///
+ [Fact]
+ public async Task InsertRange_returns_success()
+ {
+ //arrange
+ var well = await dbContext.Wells.FirstAsync();
+ var entity = CreateWellOperation(well.Id);
+ var dtos = new[] { entity.Adapt() };
- //act
- var response = await client.InsertRangeAsync(well.Id, false, dtos);
+ //act
+ var response = await client.InsertRangeAsync(well.Id, false, dtos);
- //assert
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- }
+ //assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
- ///
- /// Успешное добавление операций (с предварительной очисткой данных)
- ///
- ///
- [Fact]
- public async Task InsertRangeWithDeleteBefore_returns_success()
- {
- //arrange
- var well = await dbContext.Wells.FirstAsync();
- var entity = CreateWellOperation(well.Id);
- var dtos = new[] { entity.Adapt() };
+ ///
+ /// Успешное добавление операций (с предварительной очисткой данных)
+ ///
+ ///
+ [Fact]
+ public async Task InsertRangeWithDeleteBefore_returns_success()
+ {
+ //arrange
+ var well = await dbContext.Wells.FirstAsync();
+ var entity = CreateWellOperation(well.Id);
+ var dtos = new[] { entity.Adapt() };
- //act
- var response = await client.InsertRangeAsync(well.Id, true, dtos);
+ //act
+ var response = await client.InsertRangeAsync(well.Id, true, dtos);
- //assert
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- }
+ //assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
- ///
- /// Успешное обновление операций
- ///
- ///
- [Fact]
- public async Task UpdateRangeAsync_returns_success()
- {
- //arrange
- var well = await dbContext.Wells.FirstAsync();
- var entity = CreateWellOperation(well.Id);
- dbContext.WellOperations.Add(entity);
- await dbContext.SaveChangesAsync();
+ ///
+ /// Успешное обновление операций
+ ///
+ ///
+ [Fact]
+ public async Task UpdateRangeAsync_returns_success()
+ {
+ //arrange
+ var well = await dbContext.Wells.FirstAsync();
+ var entity = CreateWellOperation(well.Id);
+ dbContext.WellOperations.Add(entity);
+ await dbContext.SaveChangesAsync();
- var dtos = new[] { entity.Adapt() };
+ var dtos = new[] { entity.Adapt() };
- //act
- var response = await client.UpdateRangeAsync(well.Id, dtos);
+ //act
+ var response = await client.UpdateRangeAsync(well.Id, dtos);
- //assert
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- }
+ //assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
- ///
- /// Получение плановых операций
- ///
- ///
- [Fact]
- public async Task GetPageOperationsAsync_returns_first_page()
- {
- //arrange
- const int pageSize = 10;
- const int pageIndex = 0;
-
- var well = await dbContext.Wells.FirstAsync();
- var entity = CreateWellOperation(well.Id);
- dbContext.WellOperations.Add(entity);
- await dbContext.SaveChangesAsync();
+ ///
+ /// Получение плановых операций
+ ///
+ ///
+ [Fact]
+ public async Task GetPageOperationsAsync_returns_first_page()
+ {
+ //arrange
+ const int pageSize = 10;
+ const int pageIndex = 0;
- var dto = entity.Adapt();
- var timezoneOffset = TimeSpan.FromHours(well.Timezone.Hours);
- dto.DateStart = dto.DateStart.ToOffset(timezoneOffset);
- dto.LastUpdateDate = dto.LastUpdateDate?.ToOffset(timezoneOffset);
+ var well = await dbContext.Wells.FirstAsync();
+ var entity = CreateWellOperation(well.Id);
+ dbContext.WellOperations.Add(entity);
+ await dbContext.SaveChangesAsync();
- var request = new WellOperationRequestBase
- {
- OperationType = WellOperation.IdOperationTypePlan,
- Skip = pageIndex,
- Take = pageSize,
- };
+ var dto = entity.Adapt();
+ var timezoneOffset = TimeSpan.FromHours(well.Timezone.Hours);
+ dto.DateStart = dto.DateStart.ToOffset(timezoneOffset);
+ dto.LastUpdateDate = dto.LastUpdateDate?.ToOffset(timezoneOffset);
- //act
- var response = await client.GetPageOperationsAsync(well.Id, request);
+ var request = new WellOperationRequestBase
+ {
+ OperationType = WellOperation.IdOperationTypePlan,
+ Skip = pageIndex,
+ Take = pageSize,
+ };
- //assert
- Assert.Equal(response.StatusCode, HttpStatusCode.OK);
- Assert.NotNull(response.Content);
+ //act
+ var response = await client.GetPageOperationsAsync(well.Id, request);
- var totalExpected = response.Content.Count - pageSize * pageIndex;
- Assert.Equal(totalExpected, response.Content.Items.Count());
+ //assert
+ Assert.Equal(response.StatusCode, HttpStatusCode.OK);
+ Assert.NotNull(response.Content);
- Assert.Single(response.Content.Items);
- var actualDto = response.Content.Items.First();
+ var totalExpected = response.Content.Count - pageSize * pageIndex;
+ Assert.Equal(totalExpected, response.Content.Items.Count());
- MatchHelper.Match(dto, actualDto);
- }
+ Assert.Single(response.Content.Items);
+ var actualDto = response.Content.Items.First();
- [Theory]
- [InlineData(WellOperation.IdOperationTypePlan, "PlanWellOperations.xlsx")]
- [InlineData(WellOperation.IdOperationTypeFact, "FactWellOperations.xlsx")]
- public async Task ParseAsync_returns_success(int idType, string fileName)
- {
- //arrange
- var well = await dbContext.Wells.FirstAsync();
+ MatchHelper.Match(dto, actualDto);
+ }
- var expectedDto = new WellOperationDto
- {
- IdWell = well.Id,
- IdWellSectionType = 2,
- IdCategory = WellOperationCategory.IdSlide,
- IdPlan = null,
- CategoryInfo = "Доп.инфо",
- IdType = idType,
- DepthStart = 10.0,
- DepthEnd = 20.0,
- DateStart = new DateTimeOffset(new DateTime(2023, 1, 10), TimeSpan.FromHours(well.Timezone.Hours)),
- DurationHours = 1.0,
- Comment = "123",
- };
+ [Theory]
+ [InlineData(WellOperation.IdOperationTypePlan, "PlanWellOperations.xlsx")]
+ [InlineData(WellOperation.IdOperationTypeFact, "FactWellOperations.xlsx")]
+ public async Task ParseAsync_returns_success(int idType, string fileName)
+ {
+ //arrange
+ var well = await dbContext.Wells.FirstAsync();
- var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName);
+ var expectedDto = new WellOperationDto
+ {
+ IdWell = well.Id,
+ IdWellSectionType = 2,
+ IdCategory = WellOperationCategory.IdSlide,
+ IdPlan = null,
+ CategoryInfo = "Доп.инфо",
+ IdType = idType,
+ DepthStart = 10.0,
+ DepthEnd = 20.0,
+ DateStart = new DateTimeOffset(new DateTime(2023, 1, 10), TimeSpan.FromHours(well.Timezone.Hours)),
+ DurationHours = 1.0,
+ Comment = "123",
+ };
- var streamPart = new StreamPart(stream, fileName, "application/octet-stream");
+ var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName);
- //act
- var response = await client.ParseAsync(well.Id, idType, streamPart);
+ var streamPart = new StreamPart(stream, fileName, "application/octet-stream");
- //assert
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- Assert.NotNull(response.Content);
+ //act
+ var response = await client.ParseAsync(well.Id, idType, streamPart);
- var actualDto = response.Content.Item.First().Item;
+ //assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.NotNull(response.Content);
- MatchHelper.Match(expectedDto, actualDto);
- }
+ var actualDto = response.Content.Item.First().Item;
- [Theory]
- [InlineData(WellOperation.IdOperationTypePlan)]
- [InlineData(WellOperation.IdOperationTypeFact)]
- public async Task ExportAsync_returns_success(int idType)
- {
- //arrange
- var well = await dbContext.Wells.FirstAsync();
+ MatchHelper.Match(expectedDto, actualDto);
+ }
- var entity = CreateWellOperation(well.Id, idType);
- dbContext.WellOperations.Add(entity);
- await dbContext.SaveChangesAsync();
+ [Theory]
+ [InlineData(WellOperation.IdOperationTypePlan)]
+ [InlineData(WellOperation.IdOperationTypeFact)]
+ public async Task ExportAsync_returns_success(int idType)
+ {
+ //arrange
+ var well = await dbContext.Wells.FirstAsync();
- //act
- var response = await client.ExportAsync(well.Id, idType);
+ var entity = CreateWellOperation(well.Id, idType);
+ dbContext.WellOperations.Add(entity);
+ await dbContext.SaveChangesAsync();
- //assert
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- Assert.Equal("application/octet-stream", response.ContentHeaders?.ContentType?.MediaType);
- Assert.True(response.ContentHeaders?.ContentLength > 0);
- }
+ //act
+ var response = await client.ExportAsync(well.Id, idType);
+ //assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("application/octet-stream", response.ContentHeaders?.ContentType?.MediaType);
+ Assert.True(response.ContentHeaders?.ContentLength > 0);
+ }
+
+ [Theory]
+ [InlineData(WellOperation.IdOperationTypePlan)]
+ [InlineData(WellOperation.IdOperationTypeFact)]
+ public async Task MatchCategoriesBetweenTemplateAndDb_returns_success(int idType)
+ {
+ //arrange
+ var well = await dbContext.Wells.FirstAsync();
+ var responseTemplate = await client.GetTemplate(well.Id, idType);
+ var stream = responseTemplate.Content;
+
+ using var workbook = new XLWorkbook(stream);
+ var sheet = workbook.GetWorksheet("Справочники");
+
+ var count = sheet.RowsUsed().Count() - 1;
+
+ var categoryCaptionsInTemplate = new List();
+ for (var i = 0; i < count; i++)
+ {
+ var xlRow = sheet.Row(i + 2);
+ var rowNumber = xlRow.RowNumber();
+
+ var xlCell = xlRow.Cell(1);
+ var cellValue = xlCell.GetText().Trim();
+
+ categoryCaptionsInTemplate.Add(cellValue);
+ }
+
+ var responseCategories = await client.GetCategories(well.Id, false, false);
+ var categories = responseCategories.Content;
+ var categoryCaptionsInDb = categories!.Select(c => c.Name.Trim());
+
+ var notExistedInTemplate = categoryCaptionsInDb.Except(categoryCaptionsInTemplate);
+ var notExistedInDb = categoryCaptionsInTemplate.Except(categoryCaptionsInDb);
+
+ //assert
+ Assert.True(categoryCaptionsInDb.Count() == categoryCaptionsInTemplate.Count());
+ Assert.True(notExistedInTemplate.Count() == 0);
+ Assert.True(notExistedInDb.Count() == 0);
+ }
+
+ private static WellOperation CreateWellOperation(int idWell, int idType = WellOperation.IdOperationTypePlan) =>
+ new()
+ {
+ IdWell = idWell,
+ IdWellSectionType = 2,
+ IdCategory = WellOperationCategory.IdSlide,
+ IdPlan = null,
+ CategoryInfo = "Доп.инфо",
+ LastUpdateDate = new DateTimeOffset(new DateTime(2023, 1, 10)).ToUniversalTime(),
+ IdType = idType,
+ 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,
+ };
[Theory]
[InlineData(WellOperation.IdOperationTypePlan)]
[InlineData(WellOperation.IdOperationTypeFact)]
@@ -249,7 +308,7 @@ public class WellOperationControllerTest : BaseIntegrationTest
IdWellSectionType = 2,
IdCategory = WellOperationCategory.IdSlide,
IdPlan = null,
- CategoryInfo = "Доп.инфо",
+ CategoryInfo = ".",
LastUpdateDate = new DateTimeOffset(new DateTime(2023, 1, 10)).ToUniversalTime(),
IdType = idType,
DepthStart = 10.0,
diff --git a/AsbCloudWebApi.Tests/Services/WellCompositeOperation/WellCompositeOperationServiceTest.cs b/AsbCloudWebApi.Tests/Services/WellCompositeOperation/WellCompositeOperationServiceTest.cs
index 271e9c7e..c6f5f5cf 100644
--- a/AsbCloudWebApi.Tests/Services/WellCompositeOperation/WellCompositeOperationServiceTest.cs
+++ b/AsbCloudWebApi.Tests/Services/WellCompositeOperation/WellCompositeOperationServiceTest.cs
@@ -36,6 +36,7 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
new(){Id = 5036, Name = "Промывка"},
new(){Id = 5012, Name = "Подъем инструмента"},
new(){Id = 5083, Name = "Проработка принудительная"},
+ new(){Id = 5113, Name = "Бурение"},
};
private readonly static IEnumerable sectionTypes = new List()
@@ -224,7 +225,7 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
// assert
var compositeWellOperations = result.WellOperationsComposite;
Assert.Single(compositeWellOperations);
- Assert.Equal(5003, compositeWellOperations.FirstOrDefault()!.IdCategory);
+ Assert.Equal(5113, compositeWellOperations.FirstOrDefault()!.IdCategory);
Assert.Equal(1.5, compositeWellOperations.FirstOrDefault()!.DurationHours);
Assert.Equal(10, compositeWellOperations.FirstOrDefault()!.DepthStart);
}
diff --git a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
index 11d7451d..fe5f289e 100644
--- a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
+++ b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
@@ -1,4 +1,5 @@
-using AsbCloudApp.Data.DetectedOperation;
+using System;
+using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
@@ -110,7 +111,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
}
///
- /// Получить фильтрованный список операций по телеметрии САУБ
+ /// Получить список автоопределенных операций для редактирования
///
///
///
@@ -118,7 +119,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
///
[HttpGet]
[ProducesResponseType(typeof(PaginationContainer), StatusCodes.Status200OK)]
- public async Task GetAsync(int idWell, [FromQuery] DetectedOperationRequest request,
+ public async Task GetPageAsync(int idWell, [FromQuery] DetectedOperationRequest request,
CancellationToken token)
{
await AssertUserHasAccessToWellAsync(idWell, token);
@@ -133,24 +134,23 @@ namespace AsbCloudWebApi.Controllers.SAUB
var result = await detectedOperationRepository.GetPageAsync(requestToService, token);
return Ok(result);
}
-
+
///
- /// Получить статистику по фильтрованному списку операций по телеметрии САУБ
+ /// Получить статистику по автоопределенным операциям
///
///
///
///
///
[HttpGet("stat")]
- [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)]
- public async Task GetStatAsync(int idWell, [FromQuery] DetectedOperationRequest request,
- CancellationToken token)
+ [ProducesResponseType(typeof(DetectedOperationListDto), StatusCodes.Status200OK)]
+ public async Task GetAsync(int idWell, [FromQuery] DetectedOperationRequest request, CancellationToken token)
{
await AssertUserHasAccessToWellAsync(idWell, token);
-
+
var requestToService = new DetectedOperationByWellRequest(idWell, request);
- var result = await detectedOperationService.GetOperationsStatAsync(requestToService, token);
+ var result = await detectedOperationService.GetAsync(requestToService, token);
return Ok(result);
}