merge from dev

This commit is contained in:
Olga Nemt 2024-04-24 17:15:20 +05:00
commit b80b3f9a37
9 changed files with 235 additions and 162 deletions

View File

@ -72,6 +72,7 @@ namespace AsbCloudApp.Services
/// <param name="request"></param> /// <param name="request"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[Obsolete]
Task<IEnumerable<DetectedOperationStatDto>> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token); Task<IEnumerable<DetectedOperationStatDto>> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token);
/// <summary> /// <summary>

View File

@ -133,18 +133,22 @@ namespace AsbCloudInfrastructure.Repository
if (ids.Length != dtos.Count()) if (ids.Length != dtos.Count())
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь уникальные Id"); throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь уникальные Id");
var dbSet = dbContext.Set<TEntity>(); var existingEntitiesCount = await dbContext.Set<TEntity>()
var existingEntitiesCount = await dbSet
.Where(o => ids.Contains(o.Id)) .Where(o => ids.Contains(o.Id))
.CountAsync(token); .CountAsync(token);
if (ids.Length != existingEntitiesCount) if (ids.Length != existingEntitiesCount)
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны существовать в БД"); throw new ArgumentInvalidException(nameof(dtos), "Все записи должны существовать в БД");
var entities = dbContext.Set<TEntity>().Where(e => ids.Contains(e.Id)); var entities = dtos.Select(Convert);
dbContext.Set<TEntity>().UpdateRange(entities);
return await dbContext.SaveChangesAsync(token); var entries = entities.Select(entity => dbContext.Set<TEntity>().Update(entity)).ToList();
var affected = await dbContext.SaveChangesAsync(token);
entries.ForEach(entry => entry.State = EntityState.Detached);
return affected;
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@ -143,6 +143,7 @@ public class DetectedOperationService : IDetectedOperationService
} }
} }
[Obsolete]
public async Task<IEnumerable<DetectedOperationStatDto>> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token) public async Task<IEnumerable<DetectedOperationStatDto>> GetOperationsStatAsync(DetectedOperationByWellRequest request, CancellationToken token)
{ {
var well = await wellService.GetOrDefaultAsync(request.IdWell, token); var well = await wellService.GetOrDefaultAsync(request.IdWell, token);

View File

@ -96,7 +96,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
return 0; return 0;
entity.IdState = setpointsRequestDto.IdState; entity.IdState = setpointsRequestDto.IdState;
var affected = await setpointsRepository.UpdateAsync(entity, token); var affected = await setpointsRepository.UpdateRangeAsync(new[] { entity }, token);
return affected; return affected;
} }

View File

@ -274,6 +274,7 @@ namespace AsbCloudInfrastructure.Services
dto.Timezone = GetTimezone(entity.Id); dto.Timezone = GetTimezone(entity.Id);
dto.StartDate = dbContext.WellOperations.Where(e => e.IdType == WellOperation.IdOperationTypeFact) dto.StartDate = dbContext.WellOperations.Where(e => e.IdType == WellOperation.IdOperationTypeFact)
.AsNoTracking()
.MinOrDefault(e => e.DateStart)?.ToRemoteDateTime(dto.Timezone.Hours); .MinOrDefault(e => e.DateStart)?.ToRemoteDateTime(dto.Timezone.Hours);
dto.WellType = entity.WellType.Caption; dto.WellType = entity.WellType.Caption;
dto.Cluster = entity.Cluster.Caption; dto.Cluster = entity.Cluster.Caption;

View File

@ -29,4 +29,10 @@ public interface IWellOperationClient
[Get(BaseRoute + "/export")] [Get(BaseRoute + "/export")]
Task<IApiResponse<PhysicalFileResult>> ExportAsync(int idWell, int idType); Task<IApiResponse<PhysicalFileResult>> ExportAsync(int idWell, int idType);
[Get(BaseRoute + "/template")]
Task<IApiResponse<Stream>> GetTemplate(int idWell, int idType);
[Get(BaseRoute + "/categories")]
Task<IApiResponse<IEnumerable<WellOperationCategoryDto>>> GetCategories(int idWell, bool includeParents, bool includeHidden);
} }

View File

@ -1,193 +1,252 @@
using System.Net;
using System.Reflection;
using AsbCloudApp.Data.WellOperation; using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudInfrastructure;
using AsbCloudWebApi.IntegrationTests.Clients; using AsbCloudWebApi.IntegrationTests.Clients;
using AsbCloudWebApi.IntegrationTests.Data; using AsbCloudWebApi.IntegrationTests.Data;
using ClosedXML.Excel;
using Mapster; using Mapster;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Refit; using Refit;
using System.Net;
using System.Reflection;
using Xunit; using Xunit;
namespace AsbCloudWebApi.IntegrationTests.Controllers.WellOperations; namespace AsbCloudWebApi.IntegrationTests.Controllers.WellOperations;
public class WellOperationControllerTest : BaseIntegrationTest public class WellOperationControllerTest : BaseIntegrationTest
{ {
private IWellOperationClient client; private IWellOperationClient client;
public WellOperationControllerTest(WebAppFactoryFixture factory) public WellOperationControllerTest(WebAppFactoryFixture factory)
: base(factory) : base(factory)
{ {
client = factory.GetAuthorizedHttpClient<IWellOperationClient>(string.Empty); client = factory.GetAuthorizedHttpClient<IWellOperationClient>(string.Empty);
dbContext.CleanupDbSet<WellOperation>(); dbContext.CleanupDbSet<WellOperation>();
} }
/// <summary> /// <summary>
/// Успешное добавление операций (без предварительной очистки данных) /// УÑ<C2A3>пешное добавление операций (без предварительной очиÑ<C2B8>Ñки данных)
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Fact] [Fact]
public async Task InsertRange_returns_success() public async Task InsertRange_returns_success()
{ {
//arrange //arrange
var well = await dbContext.Wells.FirstAsync(); var well = await dbContext.Wells.FirstAsync();
var entity = CreateWellOperation(well.Id); var entity = CreateWellOperation(well.Id);
var dtos = new[] { entity.Adapt<WellOperationDto>() }; var dtos = new[] { entity.Adapt<WellOperationDto>() };
//act //act
var response = await client.InsertRangeAsync(well.Id, false, dtos); var response = await client.InsertRangeAsync(well.Id, false, dtos);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
} }
/// <summary> /// <summary>
/// Успешное добавление операций (с предварительной очисткой данных) /// УÑ<C2A3>пешное добавление операций (Ñ<> предварительной очиÑ<C2B8>Ñкой данных)
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Fact] [Fact]
public async Task InsertRangeWithDeleteBefore_returns_success() public async Task InsertRangeWithDeleteBefore_returns_success()
{ {
//arrange //arrange
var well = await dbContext.Wells.FirstAsync(); var well = await dbContext.Wells.FirstAsync();
var entity = CreateWellOperation(well.Id); var entity = CreateWellOperation(well.Id);
var dtos = new[] { entity.Adapt<WellOperationDto>() }; var dtos = new[] { entity.Adapt<WellOperationDto>() };
//act //act
var response = await client.InsertRangeAsync(well.Id, true, dtos); var response = await client.InsertRangeAsync(well.Id, true, dtos);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
} }
/// <summary> /// <summary>
/// Успешное обновление операций /// УÑ<C2A3>пешное обновление операций
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Fact] [Fact]
public async Task UpdateRangeAsync_returns_success() public async Task UpdateRangeAsync_returns_success()
{ {
//arrange //arrange
var well = await dbContext.Wells.FirstAsync(); var well = await dbContext.Wells.FirstAsync();
var entity = CreateWellOperation(well.Id); var entity = CreateWellOperation(well.Id);
dbContext.WellOperations.Add(entity); dbContext.WellOperations.Add(entity);
await dbContext.SaveChangesAsync(); await dbContext.SaveChangesAsync();
var dtos = new[] { entity.Adapt<WellOperationDto>() }; var dtos = new[] { entity.Adapt<WellOperationDto>() };
//act //act
var response = await client.UpdateRangeAsync(well.Id, dtos); var response = await client.UpdateRangeAsync(well.Id, dtos);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
} }
/// <summary> /// <summary>
/// Получение плановых операций /// Получение плановых операций
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Fact] [Fact]
public async Task GetPageOperationsAsync_returns_first_page() public async Task GetPageOperationsAsync_returns_first_page()
{ {
//arrange //arrange
const int pageSize = 10; const int pageSize = 10;
const int pageIndex = 0; const int pageIndex = 0;
var well = await dbContext.Wells.FirstAsync(); var well = await dbContext.Wells.FirstAsync();
var entity = CreateWellOperation(well.Id); var entity = CreateWellOperation(well.Id);
dbContext.WellOperations.Add(entity); dbContext.WellOperations.Add(entity);
await dbContext.SaveChangesAsync(); await dbContext.SaveChangesAsync();
var dto = entity.Adapt<WellOperationDto>(); var dto = entity.Adapt<WellOperationDto>();
var timezoneOffset = TimeSpan.FromHours(well.Timezone.Hours); var timezoneOffset = TimeSpan.FromHours(well.Timezone.Hours);
dto.DateStart = dto.DateStart.ToOffset(timezoneOffset); dto.DateStart = dto.DateStart.ToOffset(timezoneOffset);
dto.LastUpdateDate = dto.LastUpdateDate?.ToOffset(timezoneOffset); dto.LastUpdateDate = dto.LastUpdateDate?.ToOffset(timezoneOffset);
var request = new WellOperationRequestBase var request = new WellOperationRequestBase
{ {
OperationType = WellOperation.IdOperationTypePlan, OperationType = WellOperation.IdOperationTypePlan,
Skip = pageIndex, Skip = pageIndex,
Take = pageSize, Take = pageSize,
}; };
//act //act
var response = await client.GetPageOperationsAsync(well.Id, request); var response = await client.GetPageOperationsAsync(well.Id, request);
//assert //assert
Assert.Equal(response.StatusCode, HttpStatusCode.OK); Assert.Equal(response.StatusCode, HttpStatusCode.OK);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
var totalExpected = response.Content.Count - pageSize * pageIndex; var totalExpected = response.Content.Count - pageSize * pageIndex;
Assert.Equal(totalExpected, response.Content.Items.Count()); Assert.Equal(totalExpected, response.Content.Items.Count());
Assert.Single(response.Content.Items); Assert.Single(response.Content.Items);
var actualDto = response.Content.Items.First(); var actualDto = response.Content.Items.First();
MatchHelper.Match(dto, actualDto); MatchHelper.Match(dto, actualDto);
} }
[Theory] [Theory]
[InlineData(WellOperation.IdOperationTypePlan, "PlanWellOperations.xlsx")] [InlineData(WellOperation.IdOperationTypePlan, "PlanWellOperations.xlsx")]
[InlineData(WellOperation.IdOperationTypeFact, "FactWellOperations.xlsx")] [InlineData(WellOperation.IdOperationTypeFact, "FactWellOperations.xlsx")]
public async Task ParseAsync_returns_success(int idType, string fileName) public async Task ParseAsync_returns_success(int idType, string fileName)
{ {
//arrange //arrange
var well = await dbContext.Wells.FirstAsync(); var well = await dbContext.Wells.FirstAsync();
var expectedDto = new WellOperationDto var expectedDto = new WellOperationDto
{ {
IdWell = well.Id, IdWell = well.Id,
IdWellSectionType = 2, IdWellSectionType = 2,
IdCategory = WellOperationCategory.IdSlide, IdCategory = WellOperationCategory.IdSlide,
IdPlan = null, IdPlan = null,
CategoryInfo = "Доп.инфо", CategoryInfo = "Доп.инфо",
IdType = idType, IdType = idType,
DepthStart = 10.0, DepthStart = 10.0,
DepthEnd = 20.0, DepthEnd = 20.0,
DateStart = new DateTimeOffset(new DateTime(2023, 1, 10), TimeSpan.FromHours(well.Timezone.Hours)), DateStart = new DateTimeOffset(new DateTime(2023, 1, 10), TimeSpan.FromHours(well.Timezone.Hours)),
DurationHours = 1.0, DurationHours = 1.0,
Comment = "123", Comment = "123",
}; };
var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName); var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName);
var streamPart = new StreamPart(stream, fileName, "application/octet-stream"); var streamPart = new StreamPart(stream, fileName, "application/octet-stream");
//act //act
var response = await client.ParseAsync(well.Id, idType, streamPart); var response = await client.ParseAsync(well.Id, idType, streamPart);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
var actualDto = response.Content.Item.First().Item; var actualDto = response.Content.Item.First().Item;
MatchHelper.Match(expectedDto, actualDto); MatchHelper.Match(expectedDto, actualDto);
} }
[Theory] [Theory]
[InlineData(WellOperation.IdOperationTypePlan)] [InlineData(WellOperation.IdOperationTypePlan)]
[InlineData(WellOperation.IdOperationTypeFact)] [InlineData(WellOperation.IdOperationTypeFact)]
public async Task ExportAsync_returns_success(int idType) public async Task ExportAsync_returns_success(int idType)
{ {
//arrange //arrange
var well = await dbContext.Wells.FirstAsync(); var well = await dbContext.Wells.FirstAsync();
var entity = CreateWellOperation(well.Id, idType); var entity = CreateWellOperation(well.Id, idType);
dbContext.WellOperations.Add(entity); dbContext.WellOperations.Add(entity);
await dbContext.SaveChangesAsync(); await dbContext.SaveChangesAsync();
//act //act
var response = await client.ExportAsync(well.Id, idType); var response = await client.ExportAsync(well.Id, idType);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("application/octet-stream", response.ContentHeaders?.ContentType?.MediaType); Assert.Equal("application/octet-stream", response.ContentHeaders?.ContentType?.MediaType);
Assert.True(response.ContentHeaders?.ContentLength > 0); 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<string>();
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] [Theory]
[InlineData(WellOperation.IdOperationTypePlan)] [InlineData(WellOperation.IdOperationTypePlan)]
[InlineData(WellOperation.IdOperationTypeFact)] [InlineData(WellOperation.IdOperationTypeFact)]
@ -249,7 +308,7 @@ public class WellOperationControllerTest : BaseIntegrationTest
IdWellSectionType = 2, IdWellSectionType = 2,
IdCategory = WellOperationCategory.IdSlide, IdCategory = WellOperationCategory.IdSlide,
IdPlan = null, IdPlan = null,
CategoryInfo = "Доп.инфо", CategoryInfo = "Äîï.èíôî",
LastUpdateDate = new DateTimeOffset(new DateTime(2023, 1, 10)).ToUniversalTime(), LastUpdateDate = new DateTimeOffset(new DateTime(2023, 1, 10)).ToUniversalTime(),
IdType = idType, IdType = idType,
DepthStart = 10.0, DepthStart = 10.0,

View File

@ -36,6 +36,7 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
new(){Id = 5036, Name = "Промывка"}, new(){Id = 5036, Name = "Промывка"},
new(){Id = 5012, Name = "Подъем инструмента"}, new(){Id = 5012, Name = "Подъем инструмента"},
new(){Id = 5083, Name = "Проработка принудительная"}, new(){Id = 5083, Name = "Проработка принудительная"},
new(){Id = 5113, Name = "Бурение"},
}; };
private readonly static IEnumerable<WellSectionTypeDto> sectionTypes = new List<WellSectionTypeDto>() private readonly static IEnumerable<WellSectionTypeDto> sectionTypes = new List<WellSectionTypeDto>()
@ -224,7 +225,7 @@ namespace AsbCloudWebApi.Tests.Services.WellCompositeOperation
// assert // assert
var compositeWellOperations = result.WellOperationsComposite; var compositeWellOperations = result.WellOperationsComposite;
Assert.Single(compositeWellOperations); Assert.Single(compositeWellOperations);
Assert.Equal(5003, compositeWellOperations.FirstOrDefault()!.IdCategory); Assert.Equal(5113, compositeWellOperations.FirstOrDefault()!.IdCategory);
Assert.Equal(1.5, compositeWellOperations.FirstOrDefault()!.DurationHours); Assert.Equal(1.5, compositeWellOperations.FirstOrDefault()!.DurationHours);
Assert.Equal(10, compositeWellOperations.FirstOrDefault()!.DepthStart); Assert.Equal(10, compositeWellOperations.FirstOrDefault()!.DepthStart);
} }

View File

@ -1,4 +1,5 @@
using AsbCloudApp.Data.DetectedOperation; using System;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@ -110,7 +111,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
} }
/// <summary> /// <summary>
/// Получить фильтрованный список операций по телеметрии САУБ /// Получить список автоопределенных операций для редактирования
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <param name="request"></param> /// <param name="request"></param>
@ -118,7 +119,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(PaginationContainer<DetectedOperationDto>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(PaginationContainer<DetectedOperationDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAsync(int idWell, [FromQuery] DetectedOperationRequest request, public async Task<IActionResult> GetPageAsync(int idWell, [FromQuery] DetectedOperationRequest request,
CancellationToken token) CancellationToken token)
{ {
await AssertUserHasAccessToWellAsync(idWell, token); await AssertUserHasAccessToWellAsync(idWell, token);
@ -135,22 +136,21 @@ namespace AsbCloudWebApi.Controllers.SAUB
} }
/// <summary> /// <summary>
/// Получить статистику по фильтрованному списку операций по телеметрии САУБ /// Получить статистику по автоопределенным операциям
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <param name="request"></param> /// <param name="request"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("stat")] [HttpGet("stat")]
[ProducesResponseType(typeof(IEnumerable<DetectedOperationStatDto>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(DetectedOperationListDto), StatusCodes.Status200OK)]
public async Task<IActionResult> GetStatAsync(int idWell, [FromQuery] DetectedOperationRequest request, public async Task<IActionResult> GetAsync(int idWell, [FromQuery] DetectedOperationRequest request, CancellationToken token)
CancellationToken token)
{ {
await AssertUserHasAccessToWellAsync(idWell, token); await AssertUserHasAccessToWellAsync(idWell, token);
var requestToService = new DetectedOperationByWellRequest(idWell, request); var requestToService = new DetectedOperationByWellRequest(idWell, request);
var result = await detectedOperationService.GetOperationsStatAsync(requestToService, token); var result = await detectedOperationService.GetAsync(requestToService, token);
return Ok(result); return Ok(result);
} }