diff --git a/AsbCloudApp/Data/WellGroupOpertionDto.cs b/AsbCloudApp/Data/WellGroupOpertionDto.cs
new file mode 100644
index 00000000..9100a21b
--- /dev/null
+++ b/AsbCloudApp/Data/WellGroupOpertionDto.cs
@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace AsbCloudApp.Data;
+#nullable enable
+///
+/// Модель группированных операций по скважине
+///
+public class WellGroupOpertionDto
+{
+ ///
+ /// Id категории
+ ///
+ public int IdCategory { get; set; }
+
+ ///
+ /// Название категории
+ ///
+ public string Category { get; set; } = string.Empty;
+
+ ///
+ /// Идентификатор родителя
+ ///
+ [JsonIgnore]
+ public int? IdParent { get; set; }
+
+ ///
+ /// Количество операций
+ ///
+ public int Count { get; set; }
+
+ ///
+ /// Суммарное время операций, мин
+ ///
+ public double TotalMinutes { get; set; }
+
+ ///
+ /// Мин продолжительность операции, мин
+ ///
+ public double? MinutesMin { get; set; }
+
+ ///
+ /// Макс продолжительность операции, мин
+ ///
+ public double? MinutesMax { get; set; }
+
+ ///
+ /// Средняя продолжительность операции, мин
+ ///
+ public double? MinutesAverage { get; set; }
+
+ ///
+ /// Общая глубина забоя
+ ///
+ public double DeltaDepth { get; set; }
+
+ ///
+ /// дочерние операции
+ ///
+ public IEnumerable? Items { get; set; }
+}
+#nullable disable
\ No newline at end of file
diff --git a/AsbCloudApp/Data/WellOperationCategoryDto.cs b/AsbCloudApp/Data/WellOperationCategoryDto.cs
index fda6c4ee..220b3ee4 100644
--- a/AsbCloudApp/Data/WellOperationCategoryDto.cs
+++ b/AsbCloudApp/Data/WellOperationCategoryDto.cs
@@ -16,9 +16,9 @@ namespace AsbCloudApp.Data
public string Name { get; set; }
///
- /// код операции
+ /// Идентификатор родительской категории
///
- public int Code { get; set; }
+ public int? IdParent { get; set; }
///
/// Название ключевого показателя операции
diff --git a/AsbCloudApp/Services/IWellOperationService.cs b/AsbCloudApp/Services/IWellOperationService.cs
index 283bc150..a9105939 100644
--- a/AsbCloudApp/Services/IWellOperationService.cs
+++ b/AsbCloudApp/Services/IWellOperationService.cs
@@ -46,6 +46,30 @@ namespace AsbCloudApp.Services
int take = 32,
CancellationToken token = default);
+ ///
+ /// Получить статистику операции по скважине с группировкой по категориям
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> GetGroupOperationsStatAsync(
+ int idWell,
+ int? operationType = null,
+ IEnumerable sectionTypeIds = null,
+ IEnumerable operationCategoryIds = null,
+ DateTime begin = default,
+ DateTime end = default,
+ double minDepth = double.MinValue,
+ double maxDepth = double.MaxValue,
+ CancellationToken token = default);
+
///
/// Получить операцию по id
///
diff --git a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs
index f1ae1144..8cb9b606 100644
--- a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs
+++ b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs
@@ -89,39 +89,16 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
{
var timezone = wellService.GetTimezone(idWell);
- var query = db.WellOperations
- .Include(s => s.WellSectionType)
- .Include(s => s.OperationCategory)
- .Where(s => s.IdWell == idWell);
-
- var dateStart = query.Min(o => o.DateStart);
-
- if (operationType != default)
- query = query.Where(e => e.IdType == (int)operationType);
-
- if (sectionTypeIds != default && sectionTypeIds.Any())
- query = query.Where(e => sectionTypeIds.Contains(e.IdWellSectionType));
-
- if (operationCategoryIds != default && operationCategoryIds.Any())
- query = query.Where(e => operationCategoryIds.Contains(e.IdCategory));
-
- if (minDepth != double.MinValue)
- query = query.Where(e => e.DepthEnd >= minDepth);
-
- if (maxDepth != double.MaxValue)
- query = query.Where(e => e.DepthEnd <= maxDepth);
-
- if (begin != default)
- {
- var beginOffset = begin.ToUtcDateTimeOffset(timezone.Hours);
- query = query.Where(e => e.DateStart >= beginOffset);
- }
-
- if (end != default)
- {
- var endOffset = end.ToUtcDateTimeOffset(timezone.Hours);
- query = query.Where(e => e.DateStart <= endOffset);
- }
+ var query = BuildQuery(
+ idWell,
+ operationType,
+ sectionTypeIds,
+ operationCategoryIds,
+ begin,
+ end,
+ minDepth,
+ maxDepth,
+ token);
var result = new PaginationContainer
{
@@ -129,7 +106,7 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
Take = take,
Count = await query.CountAsync(token).ConfigureAwait(false),
};
-
+ var dateStart = query.Min(o => o.DateStart);
query = query
.OrderBy(e => e.DateStart)
.ThenBy(e => e.DepthEnd)
@@ -162,6 +139,77 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
return result;
}
+ public async Task?> GetGroupOperationsStatAsync(
+ int idWell,
+ int? operationType = default,
+ IEnumerable? sectionTypeIds = default,
+ IEnumerable? operationCategoryIds = default,
+ DateTime begin = default,
+ DateTime end = default,
+ double minDepth = double.MinValue,
+ double maxDepth = double.MaxValue,
+ CancellationToken token = default)
+ {
+ var query = BuildQuery(
+ idWell,
+ operationType,
+ sectionTypeIds,
+ operationCategoryIds,
+ begin,
+ end,
+ minDepth,
+ maxDepth,
+ token);
+ if (query is null)
+ return null;
+ var entities = await query
+ .Select(o => new {
+ o.IdCategory,
+ DurationMinutes = o.DurationHours * 60,
+ DurationDepth = o.DepthEnd - o.DepthStart
+ })
+ .ToListAsync(token);
+ var parentRelationDictionary = GetCategories()
+ .ToDictionary(c => c.Id, cc => new
+ {
+ Name = cc.Name,
+ IdParent = cc.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
+ });
+ var defaultId = 0;
+ while (dtos.Any(x => x.IdParent != null))
+ {
+ defaultId--;
+ dtos = dtos
+ .GroupBy(o => o.IdParent)
+ .Select(g => new WellGroupOpertionDto
+ {
+ IdCategory = g.Key.HasValue ? g.Key.Value : defaultId,
+ Category = g.Key.HasValue ? parentRelationDictionary[g.Key.Value].Name : "unknown",
+ Count = g.Sum(o => o.Count),
+ DeltaDepth = g.Sum(o => o.DeltaDepth),
+ TotalMinutes = g.Sum(o => o.TotalMinutes),
+ Items = g.ToList(),
+ IdParent = g.Key.HasValue ? parentRelationDictionary[g.Key.Value].IdParent : defaultId,
+
+ });
+ }
+ return dtos;
+ }
+
public async Task GetAsync(int id,
CancellationToken token = default)
{
@@ -222,6 +270,61 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
}
+
+ private IQueryable BuildQuery(
+ int idWell,
+ int? operationType = default,
+ IEnumerable sectionTypeIds = default,
+ IEnumerable operationCategoryIds = default,
+ DateTime begin = default,
+ DateTime end = default,
+ double minDepth = double.MinValue,
+ double maxDepth = double.MaxValue,
+ CancellationToken token = default)
+ {
+ var timezone = wellService.GetTimezone(idWell);
+
+ var query = db.WellOperations
+ .Include(s => s.WellSectionType)
+ .Include(s => s.OperationCategory)
+ .Where(s => s.IdWell == idWell);
+
+ var dateStart = query.Min(o => o.DateStart);
+
+ if (operationType.HasValue)
+ query = query.Where(e => e.IdType == operationType.Value);
+
+ if (sectionTypeIds != default && sectionTypeIds.Any())
+ query = query.Where(e => sectionTypeIds.Contains(e.IdWellSectionType));
+
+ if (operationCategoryIds != default && operationCategoryIds.Any())
+ query = query.Where(e => operationCategoryIds.Contains(e.IdCategory));
+
+ if (minDepth != double.MinValue)
+ query = query.Where(e => e.DepthEnd >= minDepth);
+
+ if (maxDepth != double.MaxValue)
+ query = query.Where(e => e.DepthEnd <= maxDepth);
+
+ if (begin != default)
+ {
+ var beginOffset = begin.ToUtcDateTimeOffset(timezone.Hours);
+ query = query.Where(e => e.DateStart >= beginOffset);
+ }
+
+ if (end != default)
+ {
+ var endOffset = end.ToUtcDateTimeOffset(timezone.Hours);
+ query = query.Where(e => e.DateStart <= endOffset);
+ }
+
+ query = query
+ .OrderBy(e => e.DateStart)
+ .ThenBy(e => e.DepthEnd)
+ .ThenBy(e => e.Id);
+ return query;
+
+ }
}
#nullable disable
}
diff --git a/AsbCloudWebApi/Controllers/WellOperationController.cs b/AsbCloudWebApi/Controllers/WellOperationController.cs
index 96f01ea8..9d1450ff 100644
--- a/AsbCloudWebApi/Controllers/WellOperationController.cs
+++ b/AsbCloudWebApi/Controllers/WellOperationController.cs
@@ -109,6 +109,51 @@ namespace AsbCloudWebApi.Controllers
return Ok(result);
}
+ ///
+ /// Статистика операций по скважине, группированая по категориям
+ ///
+ /// id скважины
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("groupStat")]
+ [Permission]
+ [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)]
+ public async Task GetGroupOperationsAsync(
+ [FromRoute] int idWell,
+ [FromQuery] int? opertaionType = default,
+ [FromQuery] IEnumerable sectionTypeIds = default,
+ [FromQuery] IEnumerable operationCategoryIds = default,
+ [FromQuery] DateTime begin = default,
+ [FromQuery] DateTime end = default,
+ [FromQuery] double minDepth = double.MinValue,
+ [FromQuery] double maxDepth = double.MaxValue,
+ CancellationToken token = default)
+ {
+ if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
+ return Forbid();
+
+ var result = await operationService.GetGroupOperationsStatAsync(
+ idWell,
+ opertaionType,
+ sectionTypeIds,
+ operationCategoryIds,
+ begin,
+ end,
+ minDepth,
+ maxDepth,
+ token)
+ .ConfigureAwait(false);
+ return Ok(result);
+ }
+
///
/// Возвращает нужную операцию на скважине
///