Оптимизирован WellboreService.GetWellboresAsync()

Добавлен WellOperationRepository.GetSectionsAsync()
Оптимизирован WellOperationRepository.FirstOperationDate()
This commit is contained in:
Frolov-Nikita 2023-10-06 15:19:02 +05:00
parent a6c7ddc94b
commit 01f04c7ea5
No known key found for this signature in database
GPG Key ID: 719E3386D12B0760
5 changed files with 532 additions and 415 deletions

View File

@ -0,0 +1,47 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Data;
/// <summary>
/// Параметры секции определяемые по операциям из ГГД
/// </summary>
public class SectionByOperationsDto
{
/// <summary>
/// Id скважины
/// </summary>
public int IdWell { get; set; }
/// <summary>
/// 0 = план или 1 = факт или прогноз = 2
/// </summary>
public int IdType { get; set; }
/// <summary>
/// id секции скважины
/// </summary>
public int IdWellSectionType { get; set; }
/// <summary>
/// Глубина начала первой операции в секции, м
/// </summary>
[Range(0, 50_000)]
public double DepthStart { get; set; }
/// <summary>
/// Дата начала первой операции в секции
/// </summary>
public DateTimeOffset DateStart { get; set; }
/// <summary>
/// Глубина после завершения последней операции операции в секции, м
/// </summary>
[Range(0, 50_000)]
public double DepthEnd { get; set; }
/// <summary>
/// Дата после завершения последней операции операции в секции
/// </summary>
public DateTimeOffset DateEnd { get; set; }
}

View File

@ -97,5 +97,13 @@ namespace AsbCloudApp.Repositories
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token);
/// <summary>
/// Получить секции скважин из операций ГГД. Секцие поделены на плановые и фактические.
/// </summary>
/// <param name="idsWells"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<SectionByOperationsDto>> GetSectionsAsync(IEnumerable<int> idsWells, CancellationToken token);
}
}

View File

@ -0,0 +1,5 @@
# Проблемы фонового сервиса
- нет понимания по загрузке очереди работ
- нет управления сервисом. Для исключения его влияния на другие процессы сервера.
- отключать/включать целиком
- отключать/включать отдельную периодическую задачу

View File

@ -13,394 +13,452 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository
namespace AsbCloudInfrastructure.Repository;
/// <summary>
/// репозиторий операций по скважине
/// </summary>
public class WellOperationRepository : IWellOperationRepository
{
private const string KeyCacheSections = "OperationsBySectionSummarties";
private readonly IAsbCloudDbContext db;
private readonly IMemoryCache memoryCache;
private readonly IWellService wellService;
/// <summary>
/// репозиторий операций по скважине
/// </summary>
public class WellOperationRepository : IWellOperationRepository
public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService)
{
private readonly IAsbCloudDbContext db;
private readonly IMemoryCache memoryCache;
private readonly IWellService wellService;
private static Dictionary<int, DateTimeOffset?>? firstOperationsCache = null;
public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService)
{
this.db = db;
this.memoryCache = memoryCache;
this.wellService = wellService;
}
/// <inheritdoc/>
public IEnumerable<WellOperationCategoryDto> GetCategories(bool includeParents)
{
var categories = memoryCache
.GetOrCreateBasic(db.Set<WellOperationCategory>());
if (!includeParents)
{
var parentIds = categories
.Select(o => o.IdParent)
.Distinct();
categories = categories
.Where(o => !parentIds.Contains(o.Id));
}
var result = categories
.OrderBy(o => o.Name)
.Adapt<IEnumerable<WellOperationCategoryDto>>();
return result;
}
/// <inheritdoc/>
public IEnumerable<WellSectionTypeDto> GetSectionTypes() =>
memoryCache
.GetOrCreateBasic(db.Set<WellSectionType>())
.OrderBy(s => s.Order)
.Select(s => s.Adapt<WellSectionTypeDto>());
public async Task<WellOperationPlanDto> GetOperationsPlanAsync(int idWell, DateTime? currentDate, CancellationToken token)
{
var timezone = wellService.GetTimezone(idWell);
var request = new WellOperationRequest()
{
IdWell = idWell,
OperationType = WellOperation.IdOperationTypePlan,
};
var entities = await BuildQuery(request)
.AsNoTracking()
.ToArrayAsync(token)
.ConfigureAwait(false);
var dateLastAssosiatedPlanOperation = await GetDateLastAssosiatedPlanOperationAsync(idWell, currentDate, timezone.Hours, token);
var result = new WellOperationPlanDto()
{
WellOperationsPlan = entities,
DateLastAssosiatedPlanOperation = dateLastAssosiatedPlanOperation
};
return result;
}
private async Task<DateTime?> GetDateLastAssosiatedPlanOperationAsync(
int idWell,
DateTime? lessThenDate,
double timeZoneHours,
CancellationToken token)
{
if (lessThenDate is null)
return null;
var currentDateOffset = lessThenDate.Value.ToUtcDateTimeOffset(timeZoneHours);
var timeZoneOffset = TimeSpan.FromHours(timeZoneHours);
var lastFactOperation = await db.WellOperations
.Where(o => o.IdWell == idWell)
.Where(o => o.IdType == WellOperation.IdOperationTypeFact)
.Where(o => o.IdPlan != null)
.Where(o => o.DateStart < currentDateOffset)
.Include(x => x.OperationPlan)
.OrderByDescending(x => x.DateStart)
.FirstOrDefaultAsync(token)
.ConfigureAwait(false);
if (lastFactOperation is not null)
return DateTime.SpecifyKind(lastFactOperation.OperationPlan.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified);
return null;
}
/// <inheritdoc/>
public DateTimeOffset? FirstOperationDate(int idWell)
{
if (firstOperationsCache is null)
{
var query = db.WellOperations
.GroupBy(o => o.IdWell)
.Select(g => new Tuple<int, DateTimeOffset?, DateTimeOffset?>
(
g.Key,
g.Where(o => o.IdType == WellOperation.IdOperationTypePlan).Min(o => o.DateStart),
g.Where(o => o.IdType == WellOperation.IdOperationTypeFact).Min(o => o.DateStart)
));
firstOperationsCache = query
.ToDictionary(f => f.Item1, f => f.Item3 ?? f.Item2);
}
return firstOperationsCache?.GetValueOrDefault(idWell);
}
/// <inheritdoc/>
public async Task<IEnumerable<WellOperationDto>> GetAsync(
WellOperationRequest request,
CancellationToken token)
{
var query = BuildQuery(request)
.AsNoTracking();
var result = await query.ToArrayAsync(token);
return result;
}
/// <inheritdoc/>
public async Task<PaginationContainer<WellOperationDto>> GetPageAsync(
WellOperationRequest request,
CancellationToken token)
{
var query = BuildQuery(request)
.AsNoTracking();
var result = new PaginationContainer<WellOperationDto>
{
Skip = request.Skip ?? 0,
Take = request.Take ?? 32,
Count = await query.CountAsync(token).ConfigureAwait(false),
};
query = query
.Skip(result.Skip)
.Take(result.Take);
result.Items = await query.ToArrayAsync(token);
return result;
}
/// <inheritdoc/>
public async Task<WellOperationDto?> GetOrDefaultAsync(int id,
CancellationToken token)
{
var entity = await db.WellOperations
.Include(s => s.WellSectionType)
.Include(s => s.OperationCategory)
.FirstOrDefaultAsync(e => e.Id == id, token)
.ConfigureAwait(false);
if (entity is null)
return null;
var timezone = wellService.GetTimezone(entity.IdWell);
var dto = entity.Adapt<WellOperationDto>();
dto.WellSectionTypeName = entity.WellSectionType.Caption;
dto.DateStart = entity.DateStart.ToRemoteDateTime(timezone.Hours);
dto.CategoryName = entity.OperationCategory.Name;
return dto;
}
/// <inheritdoc/>
public async Task<IEnumerable<WellGroupOpertionDto>> GetGroupOperationsStatAsync(
WellOperationRequest request,
CancellationToken token)
{
// TODO: Rename controller method
request.OperationType = WellOperation.IdOperationTypeFact;
var query = BuildQuery(request);
var entities = await query
.Select(o => new
{
o.IdCategory,
DurationMinutes = o.DurationHours * 60,
DurationDepth = o.DepthEnd - o.DepthStart
})
.ToListAsync(token);
var parentRelationDictionary = GetCategories(true)
.ToDictionary(c => c.Id, c => new
{
c.Name,
c.IdParent
});
var dtos = entities
.GroupBy(o => o.IdCategory)
.Select(g => new WellGroupOpertionDto
{
IdCategory = g.Key,
Category = parentRelationDictionary[g.Key].Name,
Count = g.Count(),
MinutesAverage = g.Average(o => o.DurationMinutes),
MinutesMin = g.Min(o => o.DurationMinutes),
MinutesMax = g.Max(o => o.DurationMinutes),
TotalMinutes = g.Sum(o => o.DurationMinutes),
DeltaDepth = g.Sum(o => o.DurationDepth),
IdParent = parentRelationDictionary[g.Key].IdParent
});
while (dtos.All(x => x.IdParent != null))
{
dtos = dtos
.GroupBy(o => o.IdParent!)
.Select(g => {
var idCategory = g.Key ?? int.MinValue;
var category = parentRelationDictionary.GetValueOrDefault(idCategory);
var newDto = new WellGroupOpertionDto
{
IdCategory = idCategory,
Category = category?.Name ?? "unknown",
Count = g.Sum(o => o.Count),
DeltaDepth = g.Sum(o => o.DeltaDepth),
TotalMinutes = g.Sum(o => o.TotalMinutes),
Items = g.ToList(),
IdParent = category?.IdParent,
};
return newDto;
});
}
return dtos;
}
/// <inheritdoc/>
public async Task<int> InsertRangeAsync(
IEnumerable<WellOperationDto> wellOperationDtos,
CancellationToken token)
{
var firstOperation = wellOperationDtos
.FirstOrDefault();
if (firstOperation is null)
return 0;
var idWell = firstOperation.IdWell;
var timezone = wellService.GetTimezone(idWell);
foreach (var dto in wellOperationDtos)
{
var entity = dto.Adapt<WellOperation>();
entity.Id = default;
entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours);
entity.IdWell = idWell;
db.WellOperations.Add(entity);
}
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<int> UpdateAsync(
WellOperationDto dto, CancellationToken token)
{
var timezone = wellService.GetTimezone(dto.IdWell);
var entity = dto.Adapt<WellOperation>();
entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours);
db.WellOperations.Update(entity);
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<int> DeleteAsync(IEnumerable<int> ids,
CancellationToken token)
{
var query = db.WellOperations.Where(e => ids.Contains(e.Id));
db.WellOperations.RemoveRange(query);
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
}
/// <summary>
/// В результате попрежнему требуется конвертировать дату
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private IQueryable<WellOperationDto> BuildQuery(WellOperationRequest request)
{
var timezone = wellService.GetTimezone(request.IdWell);
var timeZoneOffset = TimeSpan.FromHours(timezone.Hours);
var query = db.WellOperations
.Include(s => s.WellSectionType)
.Include(s => s.OperationCategory)
.Where(o => o.IdWell == request.IdWell);
if (request.OperationType.HasValue)
query = query.Where(e => e.IdType == request.OperationType.Value);
if (request.SectionTypeIds?.Any() == true)
query = query.Where(e => request.SectionTypeIds.Contains(e.IdWellSectionType));
if (request.OperationCategoryIds?.Any() == true)
query = query.Where(e => request.OperationCategoryIds.Contains(e.IdCategory));
if (request.GeDepth.HasValue)
query = query.Where(e => e.DepthEnd >= request.GeDepth.Value);
if (request.LeDepth.HasValue)
query = query.Where(e => e.DepthEnd <= request.LeDepth.Value);
if (request.GeDate.HasValue)
{
var geDateOffset = request.GeDate.Value.ToUtcDateTimeOffset(timezone.Hours);
query = query.Where(e => e.DateStart >= geDateOffset);
}
if (request.LtDate.HasValue)
{
var ltDateOffset = request.LtDate.Value.ToUtcDateTimeOffset(timezone.Hours);
query = query.Where(e => e.DateStart < ltDateOffset);
}
var currentWellOperations = db.WellOperations
.Where(subOp => subOp.IdWell == request.IdWell);
var wellOperationsWithCategoryNPT = currentWellOperations
.Where(subOp => subOp.IdType == 1)
.Where(subOp => WellOperationCategory.NonProductiveTimeSubIds.Contains(subOp.IdCategory));
var result = query.Select(o => new WellOperationDto
{
Id = o.Id,
IdPlan = o.IdPlan,
IdType = o.IdType,
IdWell = o.IdWell,
IdWellSectionType = o.IdWellSectionType,
IdCategory = o.IdCategory,
IdParentCategory = o.OperationCategory.IdParent,
CategoryName = o.OperationCategory.Name,
WellSectionTypeName = o.WellSectionType.Caption,
DateStart = DateTime.SpecifyKind(o.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified),
DepthStart = o.DepthStart,
DepthEnd = o.DepthEnd,
DurationHours = o.DurationHours,
CategoryInfo = o.CategoryInfo,
Comment = o.Comment,
NptHours = wellOperationsWithCategoryNPT
.Where(subOp => subOp.DateStart <= o.DateStart)
.Select(subOp => subOp.DurationHours)
.Sum(),
Day = (o.DateStart - currentWellOperations
.Where(subOp => subOp.IdType == o.IdType)
.Where(subOp => subOp.DateStart <= o.DateStart)
.Min(subOp => subOp.DateStart))
.TotalDays,
IdUser = o.IdUser,
LastUpdateDate = o.LastUpdateDate.ToOffset(TimeSpan.FromHours(timezone.Hours))
});
if (request.SortFields?.Any() == true)
{
result = result.SortBy(request.SortFields);
}
else
{
result = result
.OrderBy(e => e.DateStart)
.ThenBy(e => e.DepthEnd)
.ThenBy(e => e.Id);
};
return result;
}
this.db = db;
this.memoryCache = memoryCache;
this.wellService = wellService;
}
/// <inheritdoc/>
public IEnumerable<WellOperationCategoryDto> GetCategories(bool includeParents)
{
var categories = memoryCache
.GetOrCreateBasic(db.Set<WellOperationCategory>());
if (!includeParents)
{
var parentIds = categories
.Select(o => o.IdParent)
.Distinct();
categories = categories
.Where(o => !parentIds.Contains(o.Id));
}
var result = categories
.OrderBy(o => o.Name)
.Adapt<IEnumerable<WellOperationCategoryDto>>();
return result;
}
/// <inheritdoc/>
public IEnumerable<WellSectionTypeDto> GetSectionTypes() =>
memoryCache
.GetOrCreateBasic(db.Set<WellSectionType>())
.OrderBy(s => s.Order)
.Select(s => s.Adapt<WellSectionTypeDto>());
public async Task<WellOperationPlanDto> GetOperationsPlanAsync(int idWell, DateTime? currentDate, CancellationToken token)
{
var timezone = wellService.GetTimezone(idWell);
var request = new WellOperationRequest()
{
IdWell = idWell,
OperationType = WellOperation.IdOperationTypePlan,
};
var entities = await BuildQuery(request)
.AsNoTracking()
.ToArrayAsync(token)
.ConfigureAwait(false);
var dateLastAssosiatedPlanOperation = await GetDateLastAssosiatedPlanOperationAsync(idWell, currentDate, timezone.Hours, token);
var result = new WellOperationPlanDto()
{
WellOperationsPlan = entities,
DateLastAssosiatedPlanOperation = dateLastAssosiatedPlanOperation
};
return result;
}
private async Task<DateTime?> GetDateLastAssosiatedPlanOperationAsync(
int idWell,
DateTime? lessThenDate,
double timeZoneHours,
CancellationToken token)
{
if (lessThenDate is null)
return null;
var currentDateOffset = lessThenDate.Value.ToUtcDateTimeOffset(timeZoneHours);
var timeZoneOffset = TimeSpan.FromHours(timeZoneHours);
var lastFactOperation = await db.WellOperations
.Where(o => o.IdWell == idWell)
.Where(o => o.IdType == WellOperation.IdOperationTypeFact)
.Where(o => o.IdPlan != null)
.Where(o => o.DateStart < currentDateOffset)
.Include(x => x.OperationPlan)
.OrderByDescending(x => x.DateStart)
.FirstOrDefaultAsync(token)
.ConfigureAwait(false);
if (lastFactOperation is not null)
return DateTime.SpecifyKind(lastFactOperation.OperationPlan.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified);
return null;
}
/// <inheritdoc/>
public async Task<IEnumerable<SectionByOperationsDto>> GetSectionsAsync(IEnumerable<int> idsWells, CancellationToken token)
{
var cache = await memoryCache.GetOrCreateAsync(KeyCacheSections, async (entry) =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);
var query = db.Set<WellOperation>()
.GroupBy(operation => new
{
operation.IdWell,
operation.IdType,
operation.IdWellSectionType,
})
.Select(group => new
{
group.Key.IdWell,
group.Key.IdType,
group.Key.IdWellSectionType,
First = group
.OrderBy(operation => operation.DateStart)
.Select(operation => new
{
operation.DateStart,
operation.DepthStart,
})
.First(),
Last = group
.OrderByDescending(operation => operation.DateStart)
.Select(operation => new
{
operation.DateStart,
operation.DurationHours,
operation.DepthEnd,
})
.First(),
});
var dbData = await query.ToArrayAsync(token);
var sections = dbData.Select(
item => new SectionByOperationsDto
{
IdWell = item.IdWell,
IdType = item.IdType,
IdWellSectionType = item.IdWellSectionType,
DateStart = item.First.DateStart,
DepthStart = item.First.DepthStart,
DateEnd = item.Last.DateStart.AddHours(item.Last.DurationHours),
DepthEnd = item.Last.DepthEnd,
})
.ToArray()
.AsEnumerable();
entry.Value = sections;
return sections;
});
var sections = cache.Where(s => idsWells.Contains(s.IdWell));
return sections;
}
/// <inheritdoc/>
public DateTimeOffset? FirstOperationDate(int idWell)
{
var sections = GetSectionsAsync(new[] { idWell }, CancellationToken.None).Result;
var first = sections.FirstOrDefault(section => section.IdType == WellOperation.IdOperationTypeFact)
?? sections.FirstOrDefault(section => section.IdType == WellOperation.IdOperationTypePlan);
return first?.DateStart;
}
/// <inheritdoc/>
public async Task<IEnumerable<WellOperationDto>> GetAsync(
WellOperationRequest request,
CancellationToken token)
{
var query = BuildQuery(request)
.AsNoTracking();
var result = await query.ToArrayAsync(token);
return result;
}
/// <inheritdoc/>
public async Task<PaginationContainer<WellOperationDto>> GetPageAsync(
WellOperationRequest request,
CancellationToken token)
{
var query = BuildQuery(request)
.AsNoTracking();
var result = new PaginationContainer<WellOperationDto>
{
Skip = request.Skip ?? 0,
Take = request.Take ?? 32,
Count = await query.CountAsync(token).ConfigureAwait(false),
};
query = query
.Skip(result.Skip)
.Take(result.Take);
result.Items = await query.ToArrayAsync(token);
return result;
}
/// <inheritdoc/>
public async Task<WellOperationDto?> GetOrDefaultAsync(int id,
CancellationToken token)
{
var entity = await db.WellOperations
.Include(s => s.WellSectionType)
.Include(s => s.OperationCategory)
.FirstOrDefaultAsync(e => e.Id == id, token)
.ConfigureAwait(false);
if (entity is null)
return null;
var timezone = wellService.GetTimezone(entity.IdWell);
var dto = entity.Adapt<WellOperationDto>();
dto.WellSectionTypeName = entity.WellSectionType.Caption;
dto.DateStart = entity.DateStart.ToRemoteDateTime(timezone.Hours);
dto.CategoryName = entity.OperationCategory.Name;
return dto;
}
/// <inheritdoc/>
public async Task<IEnumerable<WellGroupOpertionDto>> GetGroupOperationsStatAsync(
WellOperationRequest request,
CancellationToken token)
{
// TODO: Rename controller method
request.OperationType = WellOperation.IdOperationTypeFact;
var query = BuildQuery(request);
var entities = await query
.Select(o => new
{
o.IdCategory,
DurationMinutes = o.DurationHours * 60,
DurationDepth = o.DepthEnd - o.DepthStart
})
.ToListAsync(token);
var parentRelationDictionary = GetCategories(true)
.ToDictionary(c => c.Id, c => new
{
c.Name,
c.IdParent
});
var dtos = entities
.GroupBy(o => o.IdCategory)
.Select(g => new WellGroupOpertionDto
{
IdCategory = g.Key,
Category = parentRelationDictionary[g.Key].Name,
Count = g.Count(),
MinutesAverage = g.Average(o => o.DurationMinutes),
MinutesMin = g.Min(o => o.DurationMinutes),
MinutesMax = g.Max(o => o.DurationMinutes),
TotalMinutes = g.Sum(o => o.DurationMinutes),
DeltaDepth = g.Sum(o => o.DurationDepth),
IdParent = parentRelationDictionary[g.Key].IdParent
});
while (dtos.All(x => x.IdParent != null))
{
dtos = dtos
.GroupBy(o => o.IdParent!)
.Select(g => {
var idCategory = g.Key ?? int.MinValue;
var category = parentRelationDictionary.GetValueOrDefault(idCategory);
var newDto = new WellGroupOpertionDto
{
IdCategory = idCategory,
Category = category?.Name ?? "unknown",
Count = g.Sum(o => o.Count),
DeltaDepth = g.Sum(o => o.DeltaDepth),
TotalMinutes = g.Sum(o => o.TotalMinutes),
Items = g.ToList(),
IdParent = category?.IdParent,
};
return newDto;
});
}
return dtos;
}
/// <inheritdoc/>
public async Task<int> InsertRangeAsync(
IEnumerable<WellOperationDto> wellOperationDtos,
CancellationToken token)
{
var firstOperation = wellOperationDtos
.FirstOrDefault();
if (firstOperation is null)
return 0;
var idWell = firstOperation.IdWell;
var timezone = wellService.GetTimezone(idWell);
foreach (var dto in wellOperationDtos)
{
var entity = dto.Adapt<WellOperation>();
entity.Id = default;
entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours);
entity.IdWell = idWell;
db.WellOperations.Add(entity);
}
var result = await db.SaveChangesAsync(token);
if (result > 0)
memoryCache.Remove(KeyCacheSections);
return result;
}
/// <inheritdoc/>
public async Task<int> UpdateAsync(
WellOperationDto dto, CancellationToken token)
{
var timezone = wellService.GetTimezone(dto.IdWell);
var entity = dto.Adapt<WellOperation>();
entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours);
db.WellOperations.Update(entity);
var result = await db.SaveChangesAsync(token);
if (result > 0)
memoryCache.Remove(KeyCacheSections);
return result;
}
/// <inheritdoc/>
public async Task<int> DeleteAsync(IEnumerable<int> ids,
CancellationToken token)
{
var query = db.WellOperations.Where(e => ids.Contains(e.Id));
db.WellOperations.RemoveRange(query);
var result = await db.SaveChangesAsync(token);
if (result > 0)
memoryCache.Remove(KeyCacheSections);
return result;
}
/// <summary>
/// В результате попрежнему требуется конвертировать дату
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private IQueryable<WellOperationDto> BuildQuery(WellOperationRequest request)
{
var timezone = wellService.GetTimezone(request.IdWell);
var timeZoneOffset = TimeSpan.FromHours(timezone.Hours);
var query = db.WellOperations
.Include(s => s.WellSectionType)
.Include(s => s.OperationCategory)
.Where(o => o.IdWell == request.IdWell);
if (request.OperationType.HasValue)
query = query.Where(e => e.IdType == request.OperationType.Value);
if (request.SectionTypeIds?.Any() == true)
query = query.Where(e => request.SectionTypeIds.Contains(e.IdWellSectionType));
if (request.OperationCategoryIds?.Any() == true)
query = query.Where(e => request.OperationCategoryIds.Contains(e.IdCategory));
if (request.GeDepth.HasValue)
query = query.Where(e => e.DepthEnd >= request.GeDepth.Value);
if (request.LeDepth.HasValue)
query = query.Where(e => e.DepthEnd <= request.LeDepth.Value);
if (request.GeDate.HasValue)
{
var geDateOffset = request.GeDate.Value.ToUtcDateTimeOffset(timezone.Hours);
query = query.Where(e => e.DateStart >= geDateOffset);
}
if (request.LtDate.HasValue)
{
var ltDateOffset = request.LtDate.Value.ToUtcDateTimeOffset(timezone.Hours);
query = query.Where(e => e.DateStart < ltDateOffset);
}
var currentWellOperations = db.WellOperations
.Where(subOp => subOp.IdWell == request.IdWell);
var wellOperationsWithCategoryNPT = currentWellOperations
.Where(subOp => subOp.IdType == 1)
.Where(subOp => WellOperationCategory.NonProductiveTimeSubIds.Contains(subOp.IdCategory));
var result = query.Select(o => new WellOperationDto
{
Id = o.Id,
IdPlan = o.IdPlan,
IdType = o.IdType,
IdWell = o.IdWell,
IdWellSectionType = o.IdWellSectionType,
IdCategory = o.IdCategory,
IdParentCategory = o.OperationCategory.IdParent,
CategoryName = o.OperationCategory.Name,
WellSectionTypeName = o.WellSectionType.Caption,
DateStart = DateTime.SpecifyKind(o.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified),
DepthStart = o.DepthStart,
DepthEnd = o.DepthEnd,
DurationHours = o.DurationHours,
CategoryInfo = o.CategoryInfo,
Comment = o.Comment,
NptHours = wellOperationsWithCategoryNPT
.Where(subOp => subOp.DateStart <= o.DateStart)
.Select(subOp => subOp.DurationHours)
.Sum(),
Day = (o.DateStart - currentWellOperations
.Where(subOp => subOp.IdType == o.IdType)
.Where(subOp => subOp.DateStart <= o.DateStart)
.Min(subOp => subOp.DateStart))
.TotalDays,
IdUser = o.IdUser,
LastUpdateDate = o.LastUpdateDate.ToOffset(TimeSpan.FromHours(timezone.Hours))
});
if (request.SortFields?.Any() == true)
{
result = result.SortBy(request.SortFields);
}
else
{
result = result
.OrderBy(e => e.DateStart)
.ThenBy(e => e.DepthEnd)
.ThenBy(e => e.Id);
};
return result;
}
}

View File

@ -35,7 +35,7 @@ public class WellboreService : IWellboreService
}
public async Task<IEnumerable<WellboreDto>> GetWellboresAsync(WellboreRequest request,
CancellationToken cancellationToken)
CancellationToken token)
{
var wellbores = new List<WellboreDto>(request.Ids.Count());
var skip = request.Skip ?? 0;
@ -44,26 +44,43 @@ public class WellboreService : IWellboreService
var sections = wellOperationRepository.GetSectionTypes()
.ToDictionary(w => w.Id, w => w);
var ids = request.Ids.GroupBy(i => i.idWell);
var ids = request.Ids.GroupBy(i => i.idWell, i => i.idSection);
var idsWells = request.Ids.Select(i => i.idWell);
var allSections = await wellOperationRepository.GetSectionsAsync(idsWells, token);
foreach (var id in ids)
{
var well = await wellService.GetOrDefaultAsync(id.Key, cancellationToken);
var well = await wellService.GetOrDefaultAsync(id.Key, token);
if (well is null)
continue;
var wellOperations = await GetFactOperationsAsync(well.Id, id.Select(i => i.idSection), cancellationToken);
var groupedOperations = wellOperations.GroupBy(o => o.IdWellSectionType);
var wellWellbores = groupedOperations.Select(group => new WellboreDto {
Id = group.Key,
Name = sections[group.Key].Caption,
var wellTimezoneOffset = TimeSpan.FromHours(well.Timezone.Hours);
var wellFactSections = allSections
.Where(section => section.IdWell == id.Key)
.Where(section => section.IdType == WellOperation.IdOperationTypeFact);
var idsSections = id
.Where(i => i.HasValue)
.Select(i => i!.Value);
if (idsSections.Any())
wellFactSections = wellFactSections
.Where(section => idsSections.Contains(section.IdWellSectionType));
var wellWellbores = wellFactSections.Select(section => new WellboreDto {
Id = section.IdWellSectionType,
Name = sections[section.IdWellSectionType].Caption,
Well = well.Adapt<WellWithTimezoneDto>(),
DateStart = group.Min(operation => operation.DateStart).ToUtcDateTimeOffset(well.Timezone.Hours).ToOffset(TimeSpan.FromHours(well.Timezone.Hours)),
DateEnd = group.Max(operation => operation.DateStart.AddHours(operation.DurationHours)).ToUtcDateTimeOffset(well.Timezone.Hours).ToOffset(TimeSpan.FromHours(well.Timezone.Hours)),
DepthStart = group.Min(operation => operation.DepthStart),
DepthEnd = group.Max(operation => operation.DepthEnd),
DateStart = section.DateStart.ToOffset(wellTimezoneOffset),
DateEnd = section.DateEnd.ToOffset(wellTimezoneOffset),
DepthStart = section.DepthStart,
DepthEnd = section.DepthEnd,
});
wellbores.AddRange(wellWellbores);
}
@ -71,22 +88,4 @@ public class WellboreService : IWellboreService
.OrderBy(w => w.Well.Id).ThenBy(w => w.Id)
.Skip(skip).Take(take);
}
private async Task<IOrderedEnumerable<WellOperationDto>> GetFactOperationsAsync(int idWell, IEnumerable<int?> idsSections,
CancellationToken cancellationToken)
{
var request = new WellOperationRequest
{
IdWell = idWell,
OperationType = WellOperation.IdOperationTypeFact,
SortFields = new[] { "DateStart asc" },
};
request.SectionTypeIds = idsSections.All(i => i.HasValue)
? idsSections.Select(i => i!.Value)
: null;
return (await wellOperationRepository.GetAsync(request, cancellationToken))
.OrderBy(o => o.DateStart);
}
}