forked from ddrilling/AsbCloudServer
Сервис для суточных отчётов
1. Добавлен сервис для суточных отчётов 2. Добавлены юнит тесты для сервиса с суточными отчётами
This commit is contained in:
parent
b92acca29b
commit
6c2feefff9
60
AsbCloudApp/Services/DailyReport/IDailyReportService.cs
Normal file
60
AsbCloudApp/Services/DailyReport/IDailyReportService.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.DailyReport;
|
||||
using AsbCloudApp.Data.DailyReport.Blocks;
|
||||
using AsbCloudApp.Requests;
|
||||
|
||||
namespace AsbCloudApp.Services.DailyReport;
|
||||
|
||||
public interface IDailyReportService
|
||||
{
|
||||
/// <summary>
|
||||
/// Создать отчёт
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="dateStart"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> InsertAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Обновить блок
|
||||
/// </summary>
|
||||
/// <param name="idDailyReport"></param>
|
||||
/// <param name="idUser"></param>
|
||||
/// <param name="editableBlock"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <typeparam name="TBlock"></typeparam>
|
||||
/// <returns></returns>
|
||||
Task<int> UpdateBlockAsync<TBlock>(int idDailyReport, int idUser, TBlock editableBlock, CancellationToken cancellationToken)
|
||||
where TBlock : EditableBlock;
|
||||
|
||||
/// <summary>
|
||||
/// Получить сформированный суточный отчёт
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="dateStart"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task<DailyReportDto> GetAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список суточных отчётов по скважине
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task<PaginationContainer<DailyReportDto>> GetAsync(int idWell, FileReportRequest request,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Получить диапазон дат по которым возможно сформировать суточный отчёты
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, CancellationToken cancellationToken);
|
||||
}
|
@ -1,241 +1,378 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.DailyReport;
|
||||
using AsbCloudApp.Data.User;
|
||||
using AsbCloudApp.Data.DailyReport;
|
||||
using AsbCloudApp.Exceptions;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudDb.Model;
|
||||
using AsbCloudDb.Model.DailyReport;
|
||||
using Mapster;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.DailyReport.Blocks;
|
||||
using AsbCloudApp.Data.DailyReport.Blocks.Sign;
|
||||
using AsbCloudApp.Data.DailyReport.Blocks.Subsystems;
|
||||
using AsbCloudApp.Data.DailyReport.Blocks.TimeBalance;
|
||||
using AsbCloudApp.Data.DailyReport.Blocks.WellOperation;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services.DailyReport;
|
||||
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
|
||||
using AsbCloudApp.Services.Subsystems;
|
||||
using AsbCloudDb.Model;
|
||||
using Mapster;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.DailyReport
|
||||
namespace AsbCloudInfrastructure.Services.DailyReport;
|
||||
|
||||
public class DailyReportService : IDailyReportService
|
||||
{
|
||||
private readonly IWellService wellService;
|
||||
private readonly ITrajectoryFactRepository trajectoryFactRepository;
|
||||
private readonly IDailyReportRepository dailyReportRepository;
|
||||
private readonly IScheduleRepository scheduleRepository;
|
||||
private readonly IWellOperationRepository wellOperationRepository;
|
||||
private readonly ISubsystemOperationTimeService subsystemOperationTimeService;
|
||||
private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingService;
|
||||
private readonly IDetectedOperationService detectedOperationService;
|
||||
|
||||
public class DailyReportService : IDailyReportService
|
||||
{
|
||||
private readonly IAsbCloudDbContext db;
|
||||
private readonly IUserRepository userRepository;
|
||||
private readonly IWellOperationRepository wellOperationRepository;
|
||||
private readonly IWellService wellService;
|
||||
private readonly DailyReportMakerExcel dailyReportMaker = new DailyReportMakerExcel();
|
||||
public DailyReportService(IWellService wellService,
|
||||
ITrajectoryFactRepository trajectoryFactRepository,
|
||||
IDailyReportRepository dailyReportRepository,
|
||||
IScheduleRepository scheduleRepository,
|
||||
IWellOperationRepository wellOperationRepository,
|
||||
ISubsystemOperationTimeService subsystemOperationTimeService,
|
||||
IProcessMapReportWellDrillingService processMapReportWellDrillingService,
|
||||
IDetectedOperationService detectedOperationService)
|
||||
{
|
||||
this.wellService = wellService;
|
||||
this.trajectoryFactRepository = trajectoryFactRepository;
|
||||
this.dailyReportRepository = dailyReportRepository;
|
||||
this.scheduleRepository = scheduleRepository;
|
||||
this.wellOperationRepository = wellOperationRepository;
|
||||
this.subsystemOperationTimeService = subsystemOperationTimeService;
|
||||
this.processMapReportWellDrillingService = processMapReportWellDrillingService;
|
||||
this.detectedOperationService = detectedOperationService;
|
||||
}
|
||||
|
||||
public DailyReportService(
|
||||
IAsbCloudDbContext db,
|
||||
IWellService wellService,
|
||||
IUserRepository userRepository,
|
||||
IWellOperationRepository wellOperationRepository)
|
||||
{
|
||||
this.db = db;
|
||||
this.wellService = wellService;
|
||||
this.userRepository = userRepository;
|
||||
this.wellOperationRepository = wellOperationRepository;
|
||||
public async Task<int> InsertAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken)
|
||||
{
|
||||
if (await dailyReportRepository.AnyAsync(idWell, dateStart, cancellationToken))
|
||||
throw new ArgumentInvalidException(nameof(dateStart), "Суточный отчёт уже существует");
|
||||
|
||||
}
|
||||
var dailyReport = new DailyReportDto
|
||||
{
|
||||
IdWell = idWell,
|
||||
DateStart = dateStart,
|
||||
};
|
||||
|
||||
public async Task<IEnumerable<DailyReportDto>> GetListAsync(int idWell, DateOnly? begin, DateOnly? end, CancellationToken token)
|
||||
{
|
||||
var well = wellService.GetOrDefault(idWell)
|
||||
?? throw new ArgumentInvalidException(nameof(idWell), "idWell doesn`t exist");
|
||||
return await dailyReportRepository.InsertAsync(dailyReport, cancellationToken);
|
||||
}
|
||||
|
||||
var query = db.DailyReports.Where(r => r.IdWell == idWell);
|
||||
public async Task<int> UpdateBlockAsync<TBlock>(int idDailyReport, int idUser, TBlock editableBlock,
|
||||
CancellationToken cancellationToken)
|
||||
where TBlock : EditableBlock
|
||||
{
|
||||
var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idDailyReport, cancellationToken)
|
||||
?? throw new ArgumentInvalidException(nameof(idDailyReport), $"Суточный отчёт с Id: {idDailyReport} не найден");
|
||||
|
||||
if (begin is not null)
|
||||
{
|
||||
query = query.Where(d => d.StartDate >= begin);
|
||||
}
|
||||
editableBlock.IdUser = idUser;
|
||||
|
||||
if (end is not null)
|
||||
{
|
||||
query = query.Where(d => d.StartDate <= end);
|
||||
}
|
||||
dailyReport.DateLastUpdate = DateTime.UtcNow;
|
||||
|
||||
var entities = await query.OrderByDescending(e => e.StartDate)
|
||||
.AsNoTracking()
|
||||
.ToArrayAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
switch (editableBlock)
|
||||
{
|
||||
case SubsystemBlockDto subsystemBlock:
|
||||
dailyReport.SubsystemBlock = subsystemBlock;
|
||||
break;
|
||||
case TimeBalanceBlockDto timeBalanceBlock:
|
||||
dailyReport.TimeBalanceBlock = timeBalanceBlock;
|
||||
break;
|
||||
case SignBlockDto signBlock:
|
||||
dailyReport.SignBlock = signBlock;
|
||||
break;
|
||||
}
|
||||
|
||||
var factOperationsForDtos = await GetFactOperationsForDailyReportAsync(idWell, token);
|
||||
var userDtos = await userRepository.GetAllAsync(token);
|
||||
return await dailyReportRepository.UpdateAsync(dailyReport, cancellationToken);
|
||||
}
|
||||
|
||||
var dtos = entities.Select(entity => Convert(entity, factOperationsForDtos, userDtos));
|
||||
return dtos;
|
||||
}
|
||||
public async Task<DailyReportDto> GetAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken)
|
||||
{
|
||||
var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Получение фактических операций для суточного рапорта
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<IEnumerable<WellOperationDto>> GetFactOperationsForDailyReportAsync(int idWell, CancellationToken token)
|
||||
{
|
||||
var request = new WellOperationRequest()
|
||||
{
|
||||
IdWell = idWell,
|
||||
OperationType = WellOperation.IdOperationTypeFact,
|
||||
};
|
||||
if (well is null)
|
||||
throw new ArgumentInvalidException($"Скважина с Id: {idWell} не найдена", nameof(idWell));
|
||||
|
||||
var factOperations = await wellOperationRepository.GetAsync(request, token);
|
||||
return factOperations;
|
||||
}
|
||||
var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateStart, cancellationToken) ?? new DailyReportDto
|
||||
{
|
||||
DateStart = dateStart.Date,
|
||||
IdWell = well.Id
|
||||
};
|
||||
|
||||
public async Task<int> AddAsync(int idWell, DateOnly startDate, int idUser, CancellationToken token)
|
||||
{
|
||||
var well = wellService.GetOrDefault(idWell)
|
||||
?? throw new ArgumentInvalidException(nameof(idWell), "idWell doesn`t exist");
|
||||
var factWellOperations = await GetFactWellOperationsAsync(idWell, dailyReport.DateStart, dailyReport.DateEnd,
|
||||
cancellationToken);
|
||||
|
||||
var hasEntity = await db.DailyReports
|
||||
.AnyAsync(r => r.IdWell == idWell && r.StartDate == startDate, token);
|
||||
if (hasEntity)
|
||||
throw new ArgumentInvalidException(nameof(startDate), $"daily report on {startDate} already exists");
|
||||
dailyReport.WellName = well.Caption;
|
||||
dailyReport.WellType = well.WellType;
|
||||
dailyReport.Cluster = well.Cluster;
|
||||
dailyReport.Deposit = well.Deposit;
|
||||
dailyReport.Customer = well.Companies.FirstOrDefault(c => c.IdCompanyType == 1)?.Caption;
|
||||
dailyReport.Contractor = well.Companies.FirstOrDefault(c => c.IdCompanyType == 2)?.Caption;
|
||||
dailyReport.DepthStart = factWellOperations.Min(o => o.DepthStart);
|
||||
dailyReport.DepthEnd = factWellOperations.Max(o => o.DepthEnd);
|
||||
|
||||
var entity = new AsbCloudDb.Model.DailyReport.DailyReport
|
||||
{
|
||||
IdWell = idWell,
|
||||
StartDate = startDate,
|
||||
Info = new DailyReportInfo()
|
||||
{
|
||||
Head = CreateHeadDailyReportBlock(well, startDate, idUser)
|
||||
}
|
||||
};
|
||||
db.DailyReports.Add(entity);
|
||||
var result = await db.SaveChangesAsync(token);
|
||||
return result;
|
||||
}
|
||||
await UpdateTimeBalanceBlockAsync(dailyReport, factWellOperations, cancellationToken);
|
||||
await UpdateSubsystemBlockAsync(dailyReport, cancellationToken);
|
||||
|
||||
await AddTrajectoryBlockAsync(dailyReport, cancellationToken);
|
||||
await AddScheduleBlockAsync(dailyReport, cancellationToken);
|
||||
await AddProcessMapWellDrillingBlockAsync(dailyReport, cancellationToken);
|
||||
|
||||
public async Task<int> UpdateBlockAsync(int idWell, DateOnly startDate, ItemInfoDto dto, CancellationToken token)
|
||||
{
|
||||
var well = wellService.GetOrDefault(idWell)
|
||||
?? throw new ArgumentInvalidException(nameof(idWell), "idWell doesn`t exist");
|
||||
AddFactWellOperationBlock(dailyReport, factWellOperations);
|
||||
|
||||
var entity = await db.DailyReports.FirstOrDefaultAsync(r => r.IdWell == idWell && r.StartDate == startDate, token)
|
||||
?? throw new ArgumentInvalidException(nameof(startDate), "Daily report doesn`t exist");
|
||||
return dailyReport;
|
||||
}
|
||||
|
||||
dto.LastUpdateDate = DateTimeOffset.Now;
|
||||
if (dto is HeadDto headDto)
|
||||
entity.Info.Head = headDto.Adapt<Head>();
|
||||
if (dto is BhaDto bhaDto)
|
||||
entity.Info.Bha = bhaDto.Adapt<Bha>();
|
||||
if (dto is NoDrillingDto noDrillingDto)
|
||||
entity.Info.NoDrilling = noDrillingDto.Adapt<NoDrilling>();
|
||||
if (dto is SaubDto saubDto)
|
||||
entity.Info.Saub = saubDto.Adapt<Saub>();
|
||||
if (dto is SignDto signDto)
|
||||
entity.Info.Sign = signDto.Adapt<Sign>();
|
||||
public async Task<PaginationContainer<DailyReportDto>> GetAsync(int idWell, FileReportRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = new PaginationContainer<DailyReportDto>
|
||||
{
|
||||
Skip = request.Skip ?? 0,
|
||||
Take = request.Take ?? 10,
|
||||
Items = Enumerable.Empty<DailyReportDto>()
|
||||
};
|
||||
|
||||
db.DailyReports.Update(entity);
|
||||
var result = await db.SaveChangesAsync(token);
|
||||
return result;
|
||||
}
|
||||
var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken)
|
||||
?? throw new ArgumentInvalidException(nameof(idWell), "Скважина не найдена");
|
||||
|
||||
public async Task<Stream?> MakeReportAsync(int idWell, DateOnly date, CancellationToken token)
|
||||
{
|
||||
var stageIds = WellOperationCategory.WorkStages.Select(w => w.Id).ToArray();
|
||||
var wellOperationCategories = wellOperationRepository.GetCategories(true)
|
||||
.Where(o => o.IdParent is not null)
|
||||
.Where(o => stageIds.Contains(o.IdParent!.Value));
|
||||
var datesRange = await GetDatesRangeAsync(idWell, cancellationToken);
|
||||
|
||||
var dailyReportDto = await GetOrDefaultAsync(idWell, date, token);
|
||||
if (dailyReportDto is null)
|
||||
return null;
|
||||
if (datesRange is null)
|
||||
return result;
|
||||
|
||||
var memoryStream = dailyReportMaker.MakeReportFromBlocks(dailyReportDto, wellOperationCategories);
|
||||
var dailyReports = new List<DailyReportDto>();
|
||||
|
||||
return memoryStream;
|
||||
}
|
||||
var existingDailyReports = await dailyReportRepository.GetAsync(idWell, request, cancellationToken);
|
||||
|
||||
private async Task<DailyReportDto?> GetOrDefaultAsync(int idWell, DateOnly date, CancellationToken token)
|
||||
{
|
||||
var entity = await db.DailyReports
|
||||
.FirstOrDefaultAsync(r => r.IdWell == idWell && r.StartDate == date, token)
|
||||
?? throw new ArgumentInvalidException(nameof(date), "Daily report doesn`t exist");
|
||||
if (request.GeDate.HasValue)
|
||||
{
|
||||
var startDate = new DateTime(request.GeDate.Value.Year, request.GeDate.Value.Month,
|
||||
request.GeDate.Value.Day);
|
||||
|
||||
var factOperationsForDtos = await GetFactOperationsForDailyReportAsync(idWell, token);
|
||||
var userDtos = await userRepository.GetAllAsync(token);
|
||||
var dto = Convert(entity, factOperationsForDtos, userDtos);
|
||||
return dto;
|
||||
}
|
||||
if (startDate.Date >= datesRange.From.Date)
|
||||
datesRange.From = startDate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// конвертация данных из модели базы данных в dto
|
||||
/// </summary>
|
||||
/// <param name="entity">модель базы данных</param>
|
||||
/// <param name="factOperationsForDtos">список фактичских операций для формирования суточного рапорта</param>
|
||||
/// <param name="users">список пользователей для нахождения последнего изменившего запись</param>
|
||||
/// <returns></returns>
|
||||
private DailyReportDto Convert(
|
||||
AsbCloudDb.Model.DailyReport.DailyReport entity,
|
||||
IEnumerable<WellOperationDto> factOperationsForDtos,
|
||||
IEnumerable<UserExtendedDto> users)
|
||||
{
|
||||
var dto = entity.Info.Adapt<DailyReportDto>();
|
||||
dto.StartDate = entity.StartDate;
|
||||
if (request.LeDate.HasValue)
|
||||
{
|
||||
var finishDate = new DateTime(request.LeDate.Value.Year, request.LeDate.Value.Month,
|
||||
request.LeDate.Value.Day);
|
||||
|
||||
var dailyFactOperations = factOperationsForDtos
|
||||
.Where(o => DateOnly.FromDateTime(o.DateStart) == dto.StartDate)
|
||||
.Where(o => o.IdParentCategory is not null);
|
||||
if (finishDate.Date <= datesRange.To.Date)
|
||||
datesRange.To = finishDate;
|
||||
}
|
||||
|
||||
var lastDailyFactOperation = dailyFactOperations
|
||||
.OrderByDescending(o => o.LastUpdateDate)
|
||||
.FirstOrDefault();
|
||||
dto.TimeBalance.IdUser = lastDailyFactOperation?.IdUser;
|
||||
dto.TimeBalance.LastUpdateDate = lastDailyFactOperation?.LastUpdateDate;
|
||||
dto.TimeBalance.OperationsStat = dailyFactOperations
|
||||
.GroupBy(o => o.IdParentCategory!.Value)
|
||||
.ToDictionary(g => g.Key, g => g.Sum(o => o.DurationHours));
|
||||
if (datesRange.From.AddDays(result.Skip) <= datesRange.To)
|
||||
result.Count = (int)(Math.Ceiling((datesRange.To - DateTime.UnixEpoch).TotalDays) -
|
||||
Math.Floor((datesRange.From - DateTime.UnixEpoch).TotalDays));
|
||||
|
||||
var blocks = new ItemInfoDto[] {
|
||||
dto.Head,
|
||||
dto.Bha,
|
||||
dto.NoDrilling,
|
||||
dto.TimeBalance,
|
||||
dto.Saub,
|
||||
dto.Sign
|
||||
};
|
||||
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
if (block.IdUser is not null)
|
||||
{
|
||||
block.UserName = users.FirstOrDefault(u => u.Id == block.IdUser.Value)?.MakeDisplayName()
|
||||
?? $"userId:{block.IdUser.Value}";
|
||||
}
|
||||
}
|
||||
if (request.SortFields?.Contains("DateStart desc") == true)
|
||||
{
|
||||
for (var day = result.Skip; day - result.Skip < result.Take && datesRange.To.AddDays(-day) >= datesRange.From; day++)
|
||||
{
|
||||
var dateStart = datesRange.To.AddDays(-day);
|
||||
|
||||
return dto;
|
||||
}
|
||||
AddDailyReport(dateStart);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var day = result.Skip; day - result.Skip < result.Take && datesRange.From.AddDays(day) <= datesRange.To; day++)
|
||||
{
|
||||
var dateStart = datesRange.From.AddDays(day);
|
||||
|
||||
/// <summary>
|
||||
/// Создание блока "Заголовок" по умолчанию
|
||||
/// </summary>
|
||||
/// <param name="well"></param>
|
||||
/// <param name="startDate"></param>
|
||||
/// <param name="idUser"></param>
|
||||
/// <returns></returns>
|
||||
private Head CreateHeadDailyReportBlock(WellDto well, DateOnly startDate, int idUser)
|
||||
{
|
||||
var customer = well.Companies.FirstOrDefault(company => company.IdCompanyType == 1);
|
||||
var contractor = well.Companies.FirstOrDefault(company => company.IdCompanyType == 2);
|
||||
AddDailyReport(dateStart);
|
||||
}
|
||||
}
|
||||
|
||||
return new Head()
|
||||
{
|
||||
ReportDate = startDate,
|
||||
WellName = well.Caption,
|
||||
ClusterName = well?.Cluster ?? string.Empty,
|
||||
Customer = customer?.Caption ?? string.Empty,
|
||||
Contractor = contractor?.Caption ?? string.Empty,
|
||||
IdUser = idUser,
|
||||
LastUpdateDate = DateTimeOffset.Now,
|
||||
};
|
||||
}
|
||||
}
|
||||
result.Items = dailyReports;
|
||||
|
||||
}
|
||||
return result;
|
||||
|
||||
void AddDailyReport(DateTime dateStart)
|
||||
{
|
||||
var existingDailyReport = existingDailyReports.FirstOrDefault(d => d.IdWell == idWell &&
|
||||
d.DateStart == dateStart);
|
||||
|
||||
if (existingDailyReport is not null)
|
||||
{
|
||||
dailyReports.Add(existingDailyReport);
|
||||
return;
|
||||
}
|
||||
|
||||
dailyReports.Add(new DailyReportDto
|
||||
{
|
||||
DateStart = dateStart,
|
||||
IdWell = well.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, CancellationToken cancellationToken)
|
||||
{
|
||||
var factOperations = await GetFactWellOperationsAsync(idWell, null, null,
|
||||
cancellationToken);
|
||||
|
||||
if (!factOperations.Any())
|
||||
return null;
|
||||
|
||||
return new DatesRangeDto
|
||||
{
|
||||
From = factOperations.Min(o => o.DateStart).Date,
|
||||
To = factOperations.Max(o => o.DateStart).Date
|
||||
};
|
||||
}
|
||||
|
||||
private async Task UpdateTimeBalanceBlockAsync(DailyReportDto dailyReport, IEnumerable<WellOperationDto> factWellOperations,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
const int idWellOperationSlipsTime = 5011;
|
||||
|
||||
if (dailyReport.TimeBalanceBlock is not null)
|
||||
{
|
||||
dailyReport.TimeBalanceBlock.CountWellOperationSlipsTime = (await detectedOperationService.GetAsync(
|
||||
new DetectedOperationRequest
|
||||
{
|
||||
IdsCategories = new[] { idWellOperationSlipsTime },
|
||||
IdWell = dailyReport.IdWell,
|
||||
GtDate = dailyReport.DateStart,
|
||||
LtDate = dailyReport.DateEnd
|
||||
}, cancellationToken))?.Stats.Sum(s => s.Count);
|
||||
|
||||
dailyReport.TimeBalanceBlock.WellDepthFact = factWellOperations
|
||||
.Where(o => o.IdWellSectionType == dailyReport.TimeBalanceBlock.IdSection)
|
||||
.Sum(o => o.DepthEnd = o.DepthStart);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddTrajectoryBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
|
||||
{
|
||||
var trajectory = (await trajectoryFactRepository.GetAsync(new TrajectoryGeoFactRequest
|
||||
{
|
||||
IdWell = dailyReport.IdWell,
|
||||
GeDate = dailyReport.DateStart,
|
||||
LtDate = dailyReport.DateEnd
|
||||
}, cancellationToken)).LastOrDefault();
|
||||
|
||||
dailyReport.TrajectoryBlock = new TrajectoryBlockDto
|
||||
{
|
||||
WellboreDepth = trajectory?.WellboreDepth,
|
||||
VerticalDepth = trajectory?.VerticalDepth,
|
||||
ZenithAngle = trajectory?.ZenithAngle,
|
||||
AzimuthGeo = trajectory?.AzimuthGeo
|
||||
};
|
||||
}
|
||||
|
||||
private async Task AddScheduleBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) =>
|
||||
dailyReport.ScheduleBlock = (await scheduleRepository.GetAsync(dailyReport.IdWell, dailyReport.DateStart, cancellationToken))
|
||||
.Select(s => new ScheduleRecordDto
|
||||
{
|
||||
ShiftStart = s.ShiftStart,
|
||||
ShiftEnd = s.ShiftEnd,
|
||||
Name = s.Driller?.Name,
|
||||
Surname = s.Driller?.Surname,
|
||||
Patronymic = s.Driller?.Patronymic
|
||||
});
|
||||
|
||||
private async Task UpdateSubsystemBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
|
||||
{
|
||||
dailyReport.SubsystemBlock ??= new SubsystemBlockDto();
|
||||
|
||||
dailyReport.SubsystemBlock.Modules = await GetModulesAsync();
|
||||
|
||||
async Task<IEnumerable<SubsystemRecordDto>> GetModulesAsync()
|
||||
{
|
||||
var modules = new List<SubsystemRecordDto>();
|
||||
|
||||
var statSubsystemOperationTimePerDaily = (await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest
|
||||
{
|
||||
IdWell = dailyReport.IdWell,
|
||||
GtDate = dailyReport.DateStart,
|
||||
LtDate = dailyReport.DateEnd
|
||||
}, cancellationToken)).Select(s =>
|
||||
{
|
||||
var subsystemRecord = s.Adapt<SubsystemRecordDto>();
|
||||
subsystemRecord.IdTimeInterval = 1;
|
||||
|
||||
return subsystemRecord;
|
||||
});
|
||||
|
||||
var statSubsystemOperationTimePerWell = (await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest
|
||||
{
|
||||
IdWell = dailyReport.IdWell
|
||||
}, cancellationToken)).Select(s =>
|
||||
{
|
||||
var subsystemRecord = s.Adapt<SubsystemRecordDto>();
|
||||
subsystemRecord.IdTimeInterval = 2;
|
||||
|
||||
return subsystemRecord;
|
||||
});
|
||||
|
||||
modules.AddRange(statSubsystemOperationTimePerDaily);
|
||||
modules.AddRange(statSubsystemOperationTimePerWell);
|
||||
|
||||
if (dailyReport.SubsystemBlock?.Modules != null && dailyReport.SubsystemBlock.Modules.Any())
|
||||
modules.AddRange(dailyReport.SubsystemBlock.Modules);
|
||||
|
||||
return modules;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddProcessMapWellDrillingBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
|
||||
{
|
||||
dailyReport.ProcessMapWellDrillingBlock = (await processMapReportWellDrillingService.GetAsync(dailyReport.IdWell,
|
||||
cancellationToken)).Where(p => p.DateStart >= dailyReport.DateStart &&
|
||||
p.DateStart <= dailyReport.DateEnd &&
|
||||
p.IdMode.HasValue)
|
||||
.GroupBy(p => p.IdMode)
|
||||
.Select(g => new ProcessMapWellDrillingRecordDto
|
||||
{
|
||||
IdMode = g.Key!.Value,
|
||||
WellBoreDepth = g.Sum(p => p.DeltaDepth),
|
||||
Rop = new PlanFactDto<double?>
|
||||
{
|
||||
Plan = g.Sum(p => p.RopPlan),
|
||||
Fact = g.Sum(p => p.RopFact)
|
||||
},
|
||||
MechDrillingHours = g.Sum(p => p.MechDrillingHours)
|
||||
});
|
||||
}
|
||||
|
||||
private void AddFactWellOperationBlock(DailyReportDto dailyReport, IEnumerable<WellOperationDto> factWellOperations)
|
||||
{
|
||||
const int idWellOperationCategoryDrilling = 4001;
|
||||
|
||||
dailyReport.FactWellOperationBlock = new WellOperationBlockDto
|
||||
{
|
||||
WellOperations = factWellOperations.GroupBy(o => o.IdParentCategory)
|
||||
.Select(g => new WellOperationRecordDto
|
||||
{
|
||||
IdWellCategory = g.Key,
|
||||
DurationHours = g.Sum(o => o.DurationHours)
|
||||
}),
|
||||
|
||||
DurationHoursDrillingPerSection = factWellOperations
|
||||
.Where(o => o.IdParentCategory is idWellOperationCategoryDrilling)
|
||||
.Sum(o => o.DurationHours)
|
||||
};
|
||||
}
|
||||
|
||||
private Task<IEnumerable<WellOperationDto>> GetFactWellOperationsAsync(int idWell, DateTime? dailyReportDateStart,
|
||||
DateTime? dailyReportDateEnd, CancellationToken cancellationToken) =>
|
||||
wellOperationRepository.GetAsync(new WellOperationRequest
|
||||
{
|
||||
IdWell = idWell,
|
||||
OperationType = WellOperation.IdOperationTypeFact,
|
||||
GeDate = dailyReportDateStart,
|
||||
LtDate = dailyReportDateEnd
|
||||
}, cancellationToken);
|
||||
}
|
194
AsbCloudWebApi.Tests/ServicesTests/DailyReportServiceTest.cs
Normal file
194
AsbCloudWebApi.Tests/ServicesTests/DailyReportServiceTest.cs
Normal file
@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.DailyReport;
|
||||
using AsbCloudApp.Data.DailyReport.Blocks.Sign;
|
||||
using AsbCloudApp.Data.DailyReport.Blocks.Subsystems;
|
||||
using AsbCloudApp.Data.DailyReport.Blocks.TimeBalance;
|
||||
using AsbCloudApp.Exceptions;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
|
||||
using AsbCloudApp.Services.Subsystems;
|
||||
using AsbCloudInfrastructure.Services.DailyReport;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.ServicesTests;
|
||||
|
||||
public class DailyReportServiceTest
|
||||
{
|
||||
private const int idDailyReport = 1;
|
||||
private const int idWell = 2;
|
||||
private const int idUser = 3;
|
||||
|
||||
private readonly DateTime dateStart = new DateOnly(2023, 10, 26).ToDateTime(TimeOnly.MinValue);
|
||||
|
||||
private readonly IWellService wellServiceMock = Substitute.For<IWellService>();
|
||||
private readonly ITrajectoryFactRepository trajectoryFactRepositoryMock = Substitute.For<ITrajectoryFactRepository>();
|
||||
private readonly IDailyReportRepository dailyReportRepositoryMock = Substitute.For<IDailyReportRepository>();
|
||||
private readonly IScheduleRepository scheduleRepositoryMock = Substitute.For<IScheduleRepository>();
|
||||
private readonly IWellOperationRepository wellOperationRepositoryMock = Substitute.For<IWellOperationRepository>();
|
||||
private readonly ISubsystemOperationTimeService subsystemOperationTimeServiceMock = Substitute.For<ISubsystemOperationTimeService>();
|
||||
private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingServiceMock = Substitute.For<IProcessMapReportWellDrillingService>();
|
||||
private readonly IDetectedOperationService detectedOperationServiceMock = Substitute.For<IDetectedOperationService>();
|
||||
|
||||
private readonly DailyReportService dailyReportService;
|
||||
|
||||
private readonly DailyReportDto fakeDailyReport;
|
||||
|
||||
public DailyReportServiceTest()
|
||||
{
|
||||
fakeDailyReport = new()
|
||||
{
|
||||
Id = idDailyReport,
|
||||
IdWell = idWell,
|
||||
DateStart = dateStart
|
||||
};
|
||||
|
||||
dailyReportService = new DailyReportService(wellServiceMock,
|
||||
trajectoryFactRepositoryMock,
|
||||
dailyReportRepositoryMock,
|
||||
scheduleRepositoryMock,
|
||||
wellOperationRepositoryMock,
|
||||
subsystemOperationTimeServiceMock,
|
||||
processMapReportWellDrillingServiceMock,
|
||||
detectedOperationServiceMock);
|
||||
|
||||
dailyReportRepositoryMock.InsertAsync(Arg.Any<DailyReportDto>(), Arg.Any<CancellationToken>())
|
||||
.ReturnsForAnyArgs(idDailyReport);
|
||||
|
||||
dailyReportRepositoryMock.GetOrDefaultAsync(idDailyReport, Arg.Any<CancellationToken>())
|
||||
.Returns(fakeDailyReport);
|
||||
|
||||
dailyReportRepositoryMock.UpdateAsync(Arg.Any<DailyReportDto>(), Arg.Any<CancellationToken>())
|
||||
.ReturnsForAnyArgs(idDailyReport);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InsertAsync_ShouldReturn_ExceptionAboutDuplicate()
|
||||
{
|
||||
//arrange
|
||||
dailyReportRepositoryMock.AnyAsync(Arg.Any<int>(), Arg.Any<DateTime>(), Arg.Any<CancellationToken>())
|
||||
.ReturnsForAnyArgs(true);
|
||||
|
||||
//act
|
||||
Task Result() => dailyReportService.InsertAsync(idWell, dateStart, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
var exception = await Assert.ThrowsAsync<ArgumentInvalidException>(Result);
|
||||
Assert.Equal("Суточный отчёт уже существует", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InsertAsync_ShouldReturn_PositiveId()
|
||||
{
|
||||
//arrange
|
||||
dailyReportRepositoryMock.AnyAsync(Arg.Any<int>(), Arg.Any<DateTime>(), Arg.Any<CancellationToken>())
|
||||
.ReturnsForAnyArgs(false);
|
||||
|
||||
//act
|
||||
var result = await dailyReportService.InsertAsync(idWell, dateStart, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.Equal(idDailyReport, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateSubsystemBlock_ShouldReturn_Success()
|
||||
{
|
||||
//arrange
|
||||
var fakeSubsystemBlock = new SubsystemBlockDto
|
||||
{
|
||||
IdUser = idUser,
|
||||
WellBoreDepth = 999,
|
||||
MeasurementsPerDaily = 999,
|
||||
TotalRopPlan = 999,
|
||||
Comment = "Увеличить обороты",
|
||||
Modules = new[]
|
||||
{
|
||||
new SubsystemRecordDto
|
||||
{
|
||||
IdSubsystem = 10000,
|
||||
IdTimeInterval = 1,
|
||||
UsedTimeHours = 24,
|
||||
SumDepthInterval = 1500,
|
||||
KUsage = 100
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//act
|
||||
var result = await dailyReportService.UpdateBlockAsync(idDailyReport, idUser, fakeSubsystemBlock, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(fakeDailyReport.DateLastUpdate);
|
||||
Assert.Equal(fakeDailyReport.SubsystemBlock, fakeSubsystemBlock);
|
||||
Assert.Equal(idDailyReport, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateSignBlock_ShouldReturn_Success()
|
||||
{
|
||||
//arrange
|
||||
var fakeSignBlock = new SignBlockDto
|
||||
{
|
||||
IdUser = idUser,
|
||||
DrillingMaster = new SignRecordDto()
|
||||
{
|
||||
Name = "Иван",
|
||||
Patronymic = "Иванович",
|
||||
Surname = "Иванов"
|
||||
},
|
||||
Supervisor = new SignRecordDto()
|
||||
{
|
||||
Name = "Илья",
|
||||
Patronymic = "Ильич",
|
||||
Surname = "Бурилов"
|
||||
}
|
||||
};
|
||||
|
||||
//act
|
||||
var result = await dailyReportService.UpdateBlockAsync(idDailyReport, idUser, fakeSignBlock, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(fakeDailyReport.DateLastUpdate);
|
||||
Assert.Equal(fakeDailyReport.SignBlock, fakeSignBlock);
|
||||
Assert.Equal(idDailyReport, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateTimeBalance_ShouldReturn_Success()
|
||||
{
|
||||
//arrange
|
||||
var fakeTimeBalanceBlock = new TimeBalanceBlockDto
|
||||
{
|
||||
IdUser = idUser,
|
||||
IdSection = 1,
|
||||
WellDepthPlan = 2000,
|
||||
WellOperations = new[]
|
||||
{
|
||||
new TimeBalanceRecordDto()
|
||||
{
|
||||
DurationHours = new PlanFactDto<double>()
|
||||
{
|
||||
Fact = 100,
|
||||
Plan = 150,
|
||||
},
|
||||
DrillingDeviationPerSection = 90,
|
||||
DrillingDeviationPerDaily = 100,
|
||||
ReasonDeviation = "Отклонение"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//act
|
||||
var result = await dailyReportService.UpdateBlockAsync(idDailyReport, idUser, fakeTimeBalanceBlock, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(fakeDailyReport.DateLastUpdate);
|
||||
Assert.Equal(fakeDailyReport.TimeBalanceBlock, fakeTimeBalanceBlock);
|
||||
Assert.Equal(idDailyReport, result);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user