diff --git a/AsbCloudApp/Repositories/IWellOperationRepository.cs b/AsbCloudApp/Repositories/IWellOperationRepository.cs
index ff3bfb8e..33fbf1f5 100644
--- a/AsbCloudApp/Repositories/IWellOperationRepository.cs
+++ b/AsbCloudApp/Repositories/IWellOperationRepository.cs
@@ -132,5 +132,23 @@ namespace AsbCloudApp.Repositories
///
///
Task> ValidateWithDbAsync(IEnumerable wellOperations, CancellationToken cancellationToken);
+
+ ///
+ /// Удаление полных дубликатов операций по всем скважинам
+ ///
+ ///
+ ///
+ ///
+ Task RemoveDuplicates(Action onProgressCallback, CancellationToken token);
+
+ ///
+ /// Усечение пересекающейся последующей операции по дате и глубине забоя
+ ///
+ /// Фильтр по дате. Если хоть одна операция попадет в в фильтр, то будет обработана вся скважина, а не только эта операция
+ /// Фильтр по дате. Если хоть одна операция попадет в в фильтр, то будет обработана вся скважина, а не только эта операция
+ ///
+ ///
+ ///
+ Task TrimOverlapping(DateTimeOffset? geDate, DateTimeOffset leDate, Action onProgressCallback, CancellationToken token);
}
}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
index 398945a6..2af1127e 100644
--- a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
+++ b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
@@ -5,6 +5,8 @@ using AsbCloudApp.Services;
using AsbCloudDb;
using AsbCloudDb.Model;
using DocumentFormat.OpenXml.Spreadsheet;
+using DocumentFormat.OpenXml.Vml;
+using DocumentFormat.OpenXml.Wordprocessing;
using Irony.Parsing;
using Mapster;
using Microsoft.EntityFrameworkCore;
@@ -12,6 +14,7 @@ using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
+using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -533,59 +536,160 @@ public class WellOperationRepository : IWellOperationRepository
return dtos;
}
- public async Task RemoveDuplicates(CancellationToken token)
+ public async Task RemoveDuplicates(Action onProgressCallback, CancellationToken token)
{
- var dbset = db.Set();
- var groups = await dbset
+ IQueryable dbset = db.Set();
+ var query = dbset
.GroupBy(o => new { o.IdWell, o.IdType })
+ .Select(g => new { g.Key.IdWell, g.Key.IdType });
+
+ var groups = await query
.ToArrayAsync(token);
- foreach (var group in groups)
- await RemoveDuplicatesInGroup(group.Key.IdWell, group.Key.IdType, token);
+ var count = groups.Count();
+ var i = 0;
+ var totalRemoved = 0;
+ var total = 0;
+ foreach (var group in groups)
+ {
+ var result = await RemoveDuplicatesInGroup(group.IdWell, group.IdType, token);
+ totalRemoved += result.removed;
+ total += result.total;
+ var percent = i++ / count;
+ var message = $"RemoveDuplicates [{i} of {count}] wellId: {group.IdWell}, opType: {group.IdType}, affected: {result.removed} of {result.total}";
+ onProgressCallback?.Invoke(message, percent);
+ Trace.TraceInformation(message);
+ }
+ var messageDone = $"RemoveDuplicates done [{i} of {count}] totalAffected: {totalRemoved} of {total}";
+ Trace.TraceInformation(messageDone);
+ onProgressCallback?.Invoke(messageDone, 1);
+ return totalRemoved;
}
- private async Task RemoveDuplicatesInGroup(int idWell, int idType, CancellationToken token)
+ private async Task<(int removed, int total)> RemoveDuplicatesInGroup(int idWell, int idType, CancellationToken token)
{
var dbset = db.Set();
- var operationsEnumerator = dbset
+ var entities = await dbset
.Where(o => o.IdWell == idWell && o.IdType == idType)
.OrderBy(o => o.DateStart)
- .GetEnumerator();
+ .ToListAsync(token);
- if (!operationsEnumerator.MoveNext())
- return;
+ using var entitiesEnumerator = entities.GetEnumerator();
- var preOperation = operationsEnumerator.Current;
- while (operationsEnumerator.MoveNext())
+ if (!entitiesEnumerator.MoveNext())
+ return (0, 0);
+
+ var preEntity = entitiesEnumerator.Current;
+ while (entitiesEnumerator.MoveNext())
{
- var operation = operationsEnumerator.Current;
- if (preOperation.IsSame(operation))
- dbset.Remove(operation);
+ var entity = entitiesEnumerator.Current;
+ if (preEntity.IsSame(entity))
+ dbset.Remove(entity);
else
- preOperation = operation;
+ preEntity = entity;
}
- await db.SaveChangesAsync(token);
+ var removed = await db.SaveChangesAsync(token);
+ return (removed, entities.Count);
}
- public async Task TrimOverlapping(DateTimeOffset leDate, CancellationToken token)
+ public async Task TrimOverlapping(DateTimeOffset? geDate, DateTimeOffset leDate, Action onProgressCallback, CancellationToken token)
{
var leDateUtc = leDate.ToUniversalTime();
- var dbset = db.Set();
- var groups = await dbset
+ IQueryable query = db.Set();
+ if (geDate.HasValue)
+ {
+ var geDateUtc = geDate.Value.ToUniversalTime();
+ query = query.Where(e => e.DateStart >= geDateUtc);
+ }
+
+ var groups = await query
.GroupBy(o => new { o.IdWell, o.IdType })
.Select(g => new{
MaxDate = g.Max(o => o.DateStart),
g.Key.IdWell,
g.Key.IdType,
})
+ .Where(g => g.MaxDate <= leDateUtc)
.ToArrayAsync(token);
+ var count = groups.Count();
+ var i = 0;
+ (int takeover, int trimmed,int total) totalResult = (0, 0, 0);
foreach (var group in groups)
- await TrimOverlapping(group.IdWell, group.IdType, token);
+ {
+ var result = await TrimOverlapping(group.IdWell, group.IdType, token);
+ totalResult.takeover += result.takeover;
+ totalResult.trimmed += result.trimmed;
+ totalResult.total += result.total;
+ var percent = i++ / count;
+ var message = $"TrimOverlapping [{i} of {count}] wellId: {group.IdWell}, opType: {group.IdType}, takeover:{result.takeover}, trimmed:{result.trimmed}, of {result.total}";
+ onProgressCallback?.Invoke(message, percent);
+ Trace.TraceInformation(message);
+ }
+ var messageDone = $"TrimOverlapping done [{i} of {count}] total takeover:{totalResult.takeover}, total trimmed:{totalResult.trimmed} of {totalResult.total}";
+ Trace.TraceInformation(messageDone);
+ onProgressCallback?.Invoke(messageDone, 1);
+ return totalResult.takeover + totalResult.trimmed;
}
- private Task TrimOverlapping(int idWell, int idType, CancellationToken token)
+ private async Task<(int takeover, int trimmed, int total)> TrimOverlapping(int idWell, int idType, CancellationToken token)
{
- throw new NotImplementedException();
+ var dbset = db.Set();
+ var query = dbset
+ .Where(o => o.IdWell == idWell)
+ .Where(o => o.IdType == idType)
+ .OrderBy(o => o.DateStart)
+ .ThenBy(o => o.DepthStart);
+
+ var entities = await query
+ .ToListAsync(token);
+
+ using var entitiesEnumerator = entities.GetEnumerator();
+
+ if (!entitiesEnumerator.MoveNext())
+ return (0, 0, 0);
+
+ int takeover = 0;
+ int trimmed = 0;
+ var preEntity = entitiesEnumerator.Current;
+ while (entitiesEnumerator.MoveNext())
+ {
+ var entity = entitiesEnumerator.Current;
+ var preDepth = preEntity.DepthEnd;
+
+ if (preEntity.DepthEnd >= entity.DepthEnd)
+ {
+ dbset.Remove(entity);
+ takeover++;
+ continue;
+ }
+
+ if (preEntity.DepthEnd > entity.DepthStart)
+ {
+ entity.DepthStart = preEntity.DepthEnd;
+ trimmed++;
+ }
+
+ var preDate = preEntity.DateStart.AddHours(preEntity.DurationHours);
+
+ if (preDate >= entity.DateStart.AddHours(entity.DurationHours))
+ {
+ dbset.Remove(entity);
+ takeover++;
+ continue;
+ }
+
+ if (preDate > entity.DateStart)
+ {
+ var entityDateEnd = entity.DateStart.AddHours(entity.DurationHours);
+ entity.DateStart = preDate;
+ entity.DurationHours = (entityDateEnd - entity.DateStart).TotalHours;
+ trimmed++;
+ }
+
+ preEntity = entity;
+ }
+ var affected = await db.SaveChangesAsync(token);
+ return (takeover, trimmed, entities.Count);
}
}
diff --git a/AsbCloudWebApi/Controllers/WellOperationController.cs b/AsbCloudWebApi/Controllers/WellOperationController.cs
index 27827a62..b9b99dc8 100644
--- a/AsbCloudWebApi/Controllers/WellOperationController.cs
+++ b/AsbCloudWebApi/Controllers/WellOperationController.cs
@@ -505,6 +505,38 @@ namespace AsbCloudWebApi.Controllers
return File(stream, "application/octet-stream", fileName);
}
+ ///
+ /// Удаляет полые дубликаты операций
+ ///
+ ///
+ ///
+ [HttpPost("/api/well/wellOperations/RemoveDuplicates")]
+ [Permission]
+ [Obsolete]
+ [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
+ public async Task RemoveDuplicates(CancellationToken token)
+ {
+ var result = await operationRepository.RemoveDuplicates((_, _) => { }, token);
+ return Ok(result);
+ }
+
+ ///
+ /// Удаляет полностью пересекающиеся операции или "подрезает" более поздние их по глубине и дате.
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpPost("/api/well/wellOperations/TrimOverlapping")]
+ [Permission]
+ [Obsolete]
+ [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
+ public async Task TrimOverlapping(DateTimeOffset? geDate, DateTimeOffset leDate, CancellationToken token)
+ {
+ var result = await operationRepository.TrimOverlapping(geDate, leDate, (_, _) => { }, token);
+ return Ok(result);
+ }
+
///
/// Возвращает шаблон файла импорта
///