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

425 lines
15 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.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 IProcessMapReportWellDrillingService processMapReportWellDrillingService;
private readonly IDetectedOperationService detectedOperationService;
public DailyReportService(IWellService wellService,
ITrajectoryNnbRepository trajectoryFactNnbRepository,
IDailyReportRepository dailyReportRepository,
IScheduleRepository scheduleRepository,
IWellOperationRepository wellOperationRepository,
ISubsystemService subsystemService,
IProcessMapReportWellDrillingService processMapReportWellDrillingService,
IDetectedOperationService detectedOperationService)
{
this.wellService = wellService;
this.trajectoryFactNnbRepository = trajectoryFactNnbRepository;
this.dailyReportRepository = dailyReportRepository;
this.scheduleRepository = scheduleRepository;
this.wellOperationRepository = wellOperationRepository;
this.subsystemService = subsystemService;
this.processMapReportWellDrillingService = processMapReportWellDrillingService;
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 = DateTime.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 geDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var ltDate = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var factOperationRequest = new WellOperationRequest
{
IdWell = idWell,
OperationType = WellOperation.IdOperationTypeFact,
GeDate = geDate,
LtDate = ltDate
};
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, cancellationToken);
await UpdateSubsystemBlockAsync(dailyReport, cancellationToken);
await AddTrajectoryBlockAsync(dailyReport, cancellationToken);
await AddScheduleBlockAsync(dailyReport, cancellationToken);
await AddProcessMapWellDrillingBlockAsync(dailyReport, 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 GetDatesRangeAsync(idWell, cancellationToken);
if (datesRange is null)
return result;
var dailyReports = new List<DailyReportDto>();
if (request.GeDate.HasValue)
{
var startDate = new DateTime(request.GeDate.Value.Year, request.GeDate.Value.Month,
request.GeDate.Value.Day);
if (startDate.Date >= datesRange.From.Date)
datesRange.From = startDate;
}
if (request.LeDate.HasValue)
{
var finishDate = new DateTime(request.LeDate.Value.Year, request.LeDate.Value.Month,
request.LeDate.Value.Day);
if (finishDate.Date <= datesRange.To.Date)
datesRange.To = finishDate;
}
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)) + 1;
var existingDailyReports = await dailyReportRepository.GetAsync(idWell, request, cancellationToken);
var geDateFactWellOperation = datesRange.From.AddDays(result.Skip);
var ltDateFactWellOperation = geDateFactWellOperation.AddDays(result.Take);
var factWellOperationRequest = new WellOperationRequest
{
IdWell = idWell,
OperationType = WellOperation.IdOperationTypeFact,
GeDate = geDateFactWellOperation,
LtDate = ltDateFactWellOperation
};
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));
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));
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 = date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var leDate = date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var factWellOperationPerDay = factWellOperations.Where(o => o.DateStart.Date >= geDate &&
o.DateStart.Date <= leDate);
AddFactWellOperationBlock(dailyReport, factWellOperationPerDay);
dailyReports.Add(dailyReport);
}
}
public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, CancellationToken cancellationToken)
{
var timezone = wellService.GetTimezone(idWell);
var currentDate = DateTimeOffset.UtcNow.ToRemoteDateTime(timezone.Hours);
var factOperationDatesRange = await wellOperationRepository.GetDatesRangeAsync(idWell, WellOperation.IdOperationTypeFact,
cancellationToken);
if (factOperationDatesRange is null)
return null;
var from = (factOperationDatesRange.From.AddDays(1) <= DateTime.UtcNow ?
factOperationDatesRange.From :
currentDate.AddDays(-1))
.Date;
var to = (factOperationDatesRange.To.AddDays(1) <= DateTime.UtcNow ?
factOperationDatesRange.To :
currentDate.AddDays(-1))
.Date;
return new DatesRangeDto
{
From = from,
To = to
};
}
private async Task UpdateTimeBalanceBlockAsync(DailyReportDto dailyReport, IEnumerable<WellOperationDto> factWellOperations,
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;
var geDateStart = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var leDateEnd = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
dailyReport.TimeBalanceBlock.WellOperationSlipsTimeCount = (await detectedOperationService.GetAsync(
new DetectedOperationRequest
{
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, CancellationToken cancellationToken)
{
var geDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
var leDate = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
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, CancellationToken cancellationToken)
{
var workDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
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, 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 geDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var leDate = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
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, CancellationToken cancellationToken)
{
var geDate = dailyReport.Date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
var leDate = dailyReport.Date.AddDays(1).ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified);
dailyReport.ProcessMapWellDrillingBlock = (await processMapReportWellDrillingService.GetAsync(dailyReport.IdWell,
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.MechDrillingHours)
});
}
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().CategoryName,
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 GetDatesRangeAsync(idWell, cancellationToken);
if (datesRange is null)
return false;
var from = DateOnly.FromDateTime(datesRange.From);
var to = DateOnly.FromDateTime(datesRange.To);
return dateDailyReport >= from && dateDailyReport <= to;
}
}