DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs

383 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AsbCloudApp.Data.DailyReport;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using System;
using System.Collections.Generic;
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.Data.WellOperation;
using AsbCloudApp.Requests;
using AsbCloudApp.Services.DailyReport;
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
using AsbCloudDb.Model;
using Mapster;
namespace AsbCloudInfrastructure.Services.DailyReport;
public class DailyReportService : IDailyReportService
{
private readonly IWellService wellService;
private readonly ITrajectoryNnbRepository trajectoryFactNnbRepository;
private readonly IDailyReportRepository dailyReportRepository;
private readonly IScheduleRepository scheduleRepository;
private readonly IWellOperationRepository wellOperationRepository;
private readonly ISubsystemService subsystemService;
private readonly IProcessMapReportDrillingService processMapReportDrillingService;
private readonly IDetectedOperationService detectedOperationService;
public DailyReportService(IWellService wellService,
ITrajectoryNnbRepository trajectoryFactNnbRepository,
IDailyReportRepository dailyReportRepository,
IScheduleRepository scheduleRepository,
IWellOperationRepository wellOperationRepository,
ISubsystemService subsystemService,
IProcessMapReportDrillingService processMapReportDrillingService,
IDetectedOperationService detectedOperationService)
{
this.wellService = wellService;
this.trajectoryFactNnbRepository = trajectoryFactNnbRepository;
this.dailyReportRepository = dailyReportRepository;
this.scheduleRepository = scheduleRepository;
this.wellOperationRepository = wellOperationRepository;
this.subsystemService = subsystemService;
this.processMapReportDrillingService = processMapReportDrillingService;
this.detectedOperationService = detectedOperationService;
}
public async Task<int> UpdateOrInsertAsync<TBlock>(int idWell, DateOnly dateDailyReport, int idUser, TBlock editableBlock,
CancellationToken cancellationToken)
where TBlock : ItemInfoDto
{
if (!await IsDateDailyReportInRangeAsync(idWell, dateDailyReport, cancellationToken))
throw new ArgumentInvalidException(nameof(dateDailyReport), "Невозможно обновить суточный отчёт");
var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateDailyReport, cancellationToken) ??
new DailyReportDto
{
IdWell = idWell,
Date = dateDailyReport
};
switch (editableBlock)
{
case SubsystemBlockDto subsystemBlock:
dailyReport.SubsystemBlock = subsystemBlock;
break;
case TimeBalanceBlockDto timeBalanceBlock:
dailyReport.TimeBalanceBlock = timeBalanceBlock;
break;
case SignBlockDto signBlock:
dailyReport.SignBlock = signBlock;
break;
default:
throw new InvalidOperationException("Unexpected type of editableBlock");
}
editableBlock.IdUser = idUser;
editableBlock.LastUpdateDate = DateTime.UtcNow;
dailyReport.DateLastUpdate = DateTimeOffset.UtcNow;
if (dailyReport.Id == 0)
return await dailyReportRepository.InsertAsync(dailyReport, cancellationToken);
return await dailyReportRepository.UpdateAsync(dailyReport, cancellationToken);
}
public async Task<DailyReportDto> GetAsync(int idWell, DateOnly dateDailyReport, CancellationToken cancellationToken)
{
var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken)
?? throw new ArgumentNullException(nameof(idWell), $"Скважина с Id: {idWell} не найдена");
if (!await IsDateDailyReportInRangeAsync(idWell, dateDailyReport, cancellationToken))
throw new ArgumentInvalidException(nameof(dateDailyReport), "Невозможно получить суточный отчёт");
var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateDailyReport, cancellationToken) ?? new DailyReportDto
{
Date = dateDailyReport,
IdWell = well.Id
};
var offsetHours = wellService.GetTimezone(dailyReport.IdWell).Hours;
var geDate = new DateTimeOffset(dailyReport.Date, TimeOnly.MinValue, TimeSpan.FromHours(offsetHours));
var leDate = new DateTimeOffset(dailyReport.Date.AddDays(1), TimeOnly.MinValue, TimeSpan.FromHours(offsetHours));
var factOperationRequest = new WellOperationRequest(new []{ idWell })
{
OperationType = WellOperation.IdOperationTypeFact,
GeDate = geDate,
LeDate = leDate
};
var factWellOperations = (await wellOperationRepository.GetAsync(factOperationRequest, cancellationToken))
.OrderBy(o => o.DateStart)
.ThenBy(o => o.DepthStart);
dailyReport.WellCaption = 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.FirstOrDefault()?.DepthStart;
dailyReport.DepthEnd = factWellOperations.LastOrDefault()?.DepthEnd;
await UpdateTimeBalanceBlockAsync(dailyReport, factWellOperations, geDate, leDate, cancellationToken);
await UpdateSubsystemBlockAsync(dailyReport, geDate, leDate, cancellationToken);
await AddTrajectoryBlockAsync(dailyReport, geDate, leDate, cancellationToken);
await AddScheduleBlockAsync(dailyReport, geDate, cancellationToken);
await AddProcessMapWellDrillingBlockAsync(dailyReport, geDate, leDate, cancellationToken);
AddFactWellOperationBlock(dailyReport, factWellOperations);
return dailyReport;
}
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>()
};
var datesRange = await wellOperationRepository.GetDatesRangeAsync(idWell, WellOperation.IdOperationTypeFact, cancellationToken);
if (datesRange is null)
return result;
var dailyReports = new List<DailyReportDto>();
TimeSpan offset = wellService.GetTimezone(idWell).Offset;
if (request.GeDate.HasValue)
{
var startDate = new DateTimeOffset(request.GeDate.Value, TimeOnly.MinValue, offset);
if (startDate >= datesRange.From)
datesRange.From = startDate;
}
if (request.LeDate.HasValue)
{
var finishDate = new DateTimeOffset(request.LeDate.Value, TimeOnly.MinValue, offset);
if (finishDate <= datesRange.To)
datesRange.To = finishDate;
}
result.Count = (datesRange.To.Day - DateTimeOffset.UnixEpoch.Day) - (datesRange.From.Day - DateTimeOffset.UnixEpoch.Day);
var existingDailyReports = await dailyReportRepository.GetAsync(idWell, request, cancellationToken);
var geDateFactWellOperation = datesRange.From.AddDays(result.Skip);
var leDateFactWellOperation = geDateFactWellOperation.AddDays(result.Take);
var factWellOperationRequest = new WellOperationRequest(new[] { idWell })
{
OperationType = WellOperation.IdOperationTypeFact,
GeDate = geDateFactWellOperation,
LeDate = leDateFactWellOperation
};
var factWellOperations = await wellOperationRepository.GetAsync(factWellOperationRequest, cancellationToken);
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 dateDailyReport = DateOnly.FromDateTime(datesRange.To.AddDays(-day).DateTime);
AddDailyReport(dateDailyReport);
}
}
else
{
for (var day = result.Skip; day - result.Skip < result.Take && datesRange.From.AddDays(day) <= datesRange.To; day++)
{
var dateDailyReport = DateOnly.FromDateTime(datesRange.From.AddDays(day).DateTime);
AddDailyReport(dateDailyReport);
}
}
result.Items = dailyReports;
return result;
void AddDailyReport(DateOnly date)
{
var dailyReport = existingDailyReports.FirstOrDefault(d => d.IdWell == idWell && d.Date == date)
?? new DailyReportDto
{
Date = date,
IdWell = idWell
};
var geDate = new DateTimeOffset(date, TimeOnly.MinValue, offset);
var leDate = new DateTimeOffset(date.AddDays(1), TimeOnly.MinValue, offset);
var factWellOperationPerDay = factWellOperations.Where(o => o.DateStart >= geDate &&
o.DateStart <= leDate);
AddFactWellOperationBlock(dailyReport, factWellOperationPerDay);
dailyReports.Add(dailyReport);
}
}
private async Task UpdateTimeBalanceBlockAsync(DailyReportDto dailyReport, IEnumerable<WellOperationDto> factWellOperations,
DateTimeOffset geDateStart, DateTimeOffset leDateEnd, CancellationToken cancellationToken)
{
const int idWellOperationSlipsTime = 5011;
if (dailyReport.TimeBalanceBlock is not null)
{
dailyReport.TimeBalanceBlock.SectionName = wellOperationRepository.GetSectionTypes()
.FirstOrDefault(s => s.Id == dailyReport.TimeBalanceBlock.IdSection)?.Caption;
dailyReport.TimeBalanceBlock.WellOperationSlipsTimeCount = (await detectedOperationService.GetAsync(
new DetectedOperationByWellRequest
{
IdsCategories = new[] { idWellOperationSlipsTime },
IdWell = dailyReport.IdWell,
GeDateStart = geDateStart,
LeDateEnd = leDateEnd
}, cancellationToken))?.Stats.Sum(s => s.Count);
dailyReport.TimeBalanceBlock.WellDepth.Fact = factWellOperations
.Where(o => o.IdWellSectionType == dailyReport.TimeBalanceBlock.IdSection)
.Sum(o => o.DepthEnd - o.DepthStart);
}
}
private async Task AddTrajectoryBlockAsync(DailyReportDto dailyReport,
DateTimeOffset geDate, DateTimeOffset leDate, CancellationToken cancellationToken)
{
var trajectory = (await trajectoryFactNnbRepository.GetByRequestAsync(new TrajectoryRequest
{
IdWell = dailyReport.IdWell,
GeDate = geDate,
LeDate = leDate
}, cancellationToken)).MaxBy(t => t.WellboreDepth);
dailyReport.TrajectoryBlock = new TrajectoryBlockDto
{
WellboreDepth = trajectory?.WellboreDepth,
VerticalDepth = trajectory?.VerticalDepth,
ZenithAngle = trajectory?.ZenithAngle,
AzimuthGeo = trajectory?.AzimuthGeo
};
}
private async Task AddScheduleBlockAsync(DailyReportDto dailyReport, DateTimeOffset workDate, CancellationToken cancellationToken)
{
dailyReport.ScheduleBlock = (await scheduleRepository.GetAsync(dailyReport.IdWell, workDate, 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,
DateTimeOffset geDate, DateTimeOffset leDate, CancellationToken cancellationToken)
{
dailyReport.SubsystemBlock ??= new SubsystemBlockDto();
dailyReport.SubsystemBlock.Subsystems = await GetSubsystemsAsync();
async Task<IEnumerable<SubsystemRecordDto>> GetSubsystemsAsync()
{
var subsystemsStatPerWell = await subsystemService.GetStatAsync(new SubsystemRequest
{
IdWell = dailyReport.IdWell
}, cancellationToken);
var subsystemsStatPerDay = await subsystemService.GetStatAsync(new SubsystemRequest
{
IdWell = dailyReport.IdWell,
GeDate = geDate,
LeDate = leDate
}, cancellationToken);
var subsystems = subsystemsStatPerWell
.Select(subsystemStatPerWell => new SubsystemRecordDto
{
Name = subsystemStatPerWell.SubsystemName,
UsagePerDay = subsystemsStatPerDay.FirstOrDefault(s => s.IdSubsystem == subsystemStatPerWell.IdSubsystem)?.Adapt<SubsystemParametersDto>(),
UsagePerWell = subsystemStatPerWell.Adapt<SubsystemParametersDto>()
}).ToList();
if (dailyReport.SubsystemBlock?.Subsystems != null && dailyReport.SubsystemBlock.Subsystems.Any())
subsystems.AddRange(dailyReport.SubsystemBlock.Subsystems);
return subsystems.OrderBy(s => s.Name);
}
}
private async Task AddProcessMapWellDrillingBlockAsync(DailyReportDto dailyReport,
DateTimeOffset geDate, DateTimeOffset leDate, CancellationToken cancellationToken)
{
var request = new DataSaubStatRequest();
dailyReport.ProcessMapWellDrillingBlock = (await processMapReportDrillingService.GetAsync(dailyReport.IdWell, request,
cancellationToken)).Where(p => p.DateStart >= geDate && p.DateStart <= leDate)
.GroupBy(p => p.DrillingMode)
.Select(g => new ProcessMapWellDrillingRecordDto
{
DrillingMode = g.Key,
WellBoreDepth = g.Sum(p => p.DeltaDepth),
Rop = new PlanFactDto<double?>
{
Plan = g.Sum(p => p.Rop.Plan),
Fact = g.Sum(p => p.Rop.Fact)
},
MechDrillingHours = g.Sum(p => p.DrilledTime)
});
}
private static void AddFactWellOperationBlock(DailyReportDto dailyReport, IEnumerable<WellOperationDto> factWellOperations)
{
const int idWellOperationCategoryDrilling = 4001;
dailyReport.FactWellOperationBlock = new WellOperationBlockDto
{
WellOperations = factWellOperations.GroupBy(o => o.IdCategory)
.Select(g => new WellOperationRecordDto
{
CategoryName = g.First().OperationCategoryName,
DurationHours = g.Sum(o => o.DurationHours)
}),
SectionDrillingHours = factWellOperations
.Where(o => o.IdParentCategory is idWellOperationCategoryDrilling)
.Sum(o => o.DurationHours)
};
}
private async Task<bool> IsDateDailyReportInRangeAsync(int idWell, DateOnly dateDailyReport, CancellationToken cancellationToken)
{
var datesRange = await wellOperationRepository.GetDatesRangeAsync(idWell, WellOperation.IdOperationTypeFact, cancellationToken);
if (datesRange is null)
return false;
var from = DateOnly.FromDateTime(datesRange.From.DateTime);
var to = DateOnly.FromDateTime(datesRange.To.DateTime);
return dateDailyReport >= from && dateDailyReport <= to;
}
}