Merge branch 'dev' into feature/#26940800-add-some-well-categories

This commit is contained in:
on.nemtina 2024-04-15 10:42:17 +05:00
commit 0f75ddf524
28 changed files with 10261 additions and 364 deletions

View File

@ -8,85 +8,84 @@ namespace AsbCloudApp.Data.DetectedOperation;
/// <summary>
/// Автоматически определенная операция
/// </summary>
public class DetectedOperationDto: IId
public class DetectedOperationDto : IId
{
/// <inheritdoc/>
[Required]
public int Id { get; set; }
/// <inheritdoc/>
[Required]
public int Id { get; set; }
/// <summary>
/// Id телеметрии
/// </summary>
[Required]
public int IdTelemetry { get; set; }
/// <summary>
/// Id телеметрии
/// </summary>
[Required]
public int IdTelemetry { get; set; }
/// <summary>
/// Id названия/описания операции
/// </summary>
[Required]
public int IdCategory { get; set; }
/// <summary>
/// Id названия/описания операции
/// </summary>
[Required]
public int IdCategory { get; set; }
/// <summary>
/// Id пользователя панели на момент начала операции
/// </summary>
[Required]
public int IdUserAtStart { get; set; }
/// <summary>
/// Пользователь панели оператора
/// </summary>
public string? TelemetryUserName { get; set; }
/// <summary>
/// Id пользователя панели на момент начала операции
/// </summary>
public int? IdUserAtStart { get; set; }
/// <summary>
/// Id пользователя изменившего операцию
/// </summary>
public int? IdEditor { get; set; }
/// <summary>
/// Дата завершения операции в часовом поясе скважины
/// </summary>
[Required]
public DateTimeOffset DateEnd { get; set; }
/// <summary>
/// Дата завершения операции в часовом поясе скважины
/// </summary>
[Required]
public DateTimeOffset DateEnd { get; set; }
/// <summary>
/// Дата начала операции в часовом поясе скважины
/// </summary>
[Required]
public DateTimeOffset DateStart { get; set; }
/// <summary>
/// Дата начала операции в часовом поясе скважины
/// </summary>
[Required]
public DateTimeOffset DateStart { get; set; }
/// <summary>
/// глубина на завершения операции, м
/// </summary>
[Required]
public double DepthEnd { get; set; }
/// <summary>
/// глубина на завершения операции, м
/// </summary>
[Required]
public double DepthEnd { get; set; }
/// <summary>
/// глубина на начало операции, м
/// </summary>
[Required]
public double DepthStart { get; set; }
/// <summary>
/// глубина на начало операции, м
/// </summary>
[Required]
public double DepthStart { get; set; }
/// <summary>
/// Продолжительность операции в минутах
/// </summary>
[Required]
public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;
/// <summary>
/// Продолжительность операции в минутах
/// </summary>
[Required]
public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;
/// <summary>
/// название/описание операции
/// </summary>
[Required]
public WellOperationCategoryDto OperationCategory { get; set; } = null!;
/// <summary>
/// Включенные подсистемы
/// </summary>
[Required]
public EnabledSubsystems EnabledSubsystems { get; set; }
/// <summary>
/// Флаг включенной подсистемы
/// </summary>
[Required]
public int EnabledSubsystems { get; set; }
/// <summary>
/// Значение ключевой параметра операции
/// </summary>
[Required]
public double Value { get; set; }
/// <summary>
/// название/описание операции
/// </summary>
[Required]
public WellOperationCategoryDto OperationCategory { get; set; } = null!;
/// <summary>
/// Ключевой параметр операции
/// </summary>
[Required]
public double Value { get; set; }
/// <summary>
/// Доп. инфо по операции
/// </summary>
public IDictionary<string, object> ExtraData { get; set; } = new Dictionary<string, object>();
/// <summary>
/// Доп. инфо по операции
/// </summary>
public IDictionary<string, object> ExtraData { get; set; } = new Dictionary<string, object>();
}

View File

@ -0,0 +1,105 @@
namespace AsbCloudApp.Data.DetectedOperation;
/// <summary>
/// Включённые подсистемы
/// </summary>
public struct EnabledSubsystems
{
private int value;
private EnabledSubsystems(int value)
{
this.value = value;
}
/// <inheritdoc/>
public static implicit operator int(EnabledSubsystems param) =>
param.value;
/// <inheritdoc/>
public static implicit operator EnabledSubsystems(int param) =>
new(param);
/// <summary>
/// Бурение ротором
/// </summary>
public bool IsAutoRotor
{
get => IsEnabledSubsystem(EnabledSubsystemsFlags.AutoRotor);
set => UpdateEnabledSubsystems(value, EnabledSubsystemsFlags.AutoRotor);
}
/// <summary>
/// Бурение слайдом
/// </summary>
public bool IsAutoSlide
{
get => IsEnabledSubsystem(EnabledSubsystemsFlags.AutoSlide);
set => UpdateEnabledSubsystems(value, EnabledSubsystemsFlags.AutoSlide);
}
/// <summary>
/// ПРОРАБОТКА
/// </summary>
public bool IsAutoConditionig
{
get => IsEnabledSubsystem(EnabledSubsystemsFlags.AutoConditionig);
set => UpdateEnabledSubsystems(value, EnabledSubsystemsFlags.AutoConditionig);
}
/// <summary>
/// СПУСК СПО
/// </summary>
public bool IsAutoSinking
{
get => IsEnabledSubsystem(EnabledSubsystemsFlags.AutoSinking);
set => UpdateEnabledSubsystems(value, EnabledSubsystemsFlags.AutoSinking);
}
/// <summary>
/// ПОДЪЕМ СПО
/// </summary>
public bool IsAutoLifting
{
get => IsEnabledSubsystem(EnabledSubsystemsFlags.AutoLifting);
set => UpdateEnabledSubsystems(value, EnabledSubsystemsFlags.AutoLifting);
}
/// <summary>
/// ПОДЪЕМ С ПРОРАБОТКОЙ
/// </summary>
public bool IsAutoLiftingWithConditionig
{
get => IsEnabledSubsystem(EnabledSubsystemsFlags.AutoLiftingWithConditionig);
set => UpdateEnabledSubsystems(value, EnabledSubsystemsFlags.AutoLiftingWithConditionig);
}
/// <summary>
/// Блокировка
/// </summary>
public bool IsAutoBlocknig
{
get => IsEnabledSubsystem(EnabledSubsystemsFlags.AutoBlocknig);
set => UpdateEnabledSubsystems(value, EnabledSubsystemsFlags.AutoBlocknig);
}
/// <summary>
/// Осцилляция
/// </summary>
public bool IsAutoOscillation
{
get => IsEnabledSubsystem(EnabledSubsystemsFlags.AutoOscillation);
set => UpdateEnabledSubsystems(value, EnabledSubsystemsFlags.AutoOscillation);
}
private bool IsEnabledSubsystem(EnabledSubsystemsFlags flag) =>
(value & (int)flag) > 0;
private void UpdateEnabledSubsystems(bool isEnable, EnabledSubsystemsFlags flag)
{
if (isEnable)
value |= (int)flag;
else
value &= ~(int)flag;
}
}

View File

@ -4,24 +4,40 @@ using AsbCloudApp.Requests;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
using AsbCloudApp.Services;
using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperation;
namespace AsbCloudApp.Repositories;
/// <summary>
/// Таблица автоматически определенных операций
/// </summary>
public interface IDetectedOperationRepository : ICrudRepository<DetectedOperationDto>
public interface IDetectedOperationRepository
{
/// <summary>
/// Добавление записей
/// Добавление нескольких записей
/// </summary>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns>количество добавленных</returns>
Task<int> InsertRangeAsync(IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
/// <summary>
/// Обновить несколько записей
/// </summary>
/// <param name="idUser"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> Insert(int? idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
Task<int> UpdateRangeAsync(IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
/// <summary>
/// Удаление нескольких записей
/// </summary>
/// <param name="ids"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteRangeAsync(IEnumerable<int> ids, CancellationToken token);
/// <summary>
/// Получить автоматически определенные операции по телеметрии
/// </summary>
@ -31,40 +47,12 @@ public interface IDetectedOperationRepository : ICrudRepository<DetectedOperatio
Task<IEnumerable<DetectedOperationDto>> Get(DetectedOperationByTelemetryRequest request, CancellationToken token);
/// <summary>
/// Редактирование записей
/// Получить страницу списка операций
/// </summary>
/// <param name="idUser"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> Update(int idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
/// <summary>
/// Добавляет Dto у которых id == 0, изменяет dto у которых id != 0
/// </summary>
/// <param name="idUser"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> UpdateOrInsert(int idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
/// <summary>
/// Удалить операции
/// </summary>
/// <param name="idUser"></param>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> Delete(int idUser, DetectedOperationByTelemetryRequest request, CancellationToken token);
/// <summary>
/// Удаление записей
/// </summary>
/// <param name="idUser"></param>
/// <param name="ids"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteRange(int idUser, IEnumerable<int> ids, CancellationToken token);
Task<PaginationContainer<DetectedOperationDto>> GetPageAsync(DetectedOperationByTelemetryRequest request, CancellationToken token);
/// <summary>
/// Получение дат последних определённых операций
@ -72,4 +60,12 @@ public interface IDetectedOperationRepository : ICrudRepository<DetectedOperatio
/// <param name="token"></param>
/// <returns></returns>
Task<IDictionary<int, DateTimeOffset>> GetLastDetectedDatesAsync(CancellationToken token);
/// <summary>
/// Удалить операции
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteAsync(DetectedOperationByTelemetryRequest request, CancellationToken token);
}

View File

@ -13,6 +13,26 @@ namespace AsbCloudApp.Services
/// </summary>
public interface IDetectedOperationService
{
/// <summary>
/// Добавление операций
/// </summary>
/// <param name="idEditor"></param>
/// <param name="idWell"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> InsertRangeManualAsync(int idEditor, int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
/// <summary>
/// Редактирование операций
/// </summary>
/// <param name="idEditor"></param>
/// <param name="idWell"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> UpdateRangeManualAsync(int idEditor, int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token);
/// <summary>
/// Список названий операций.
/// Если указан idWell, то возвращается список названий операций найденных на указанной скважине.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
/// <inheritdoc />
public partial class Update_DetectedOperation : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<int>(
name: "id_user",
table: "t_detected_operation",
type: "integer",
nullable: true,
comment: "Id пользователя по телеметрии на момент начала операции",
oldClrType: typeof(int),
oldType: "integer",
oldComment: "Id пользователя по телеметрии на момент начала операции");
migrationBuilder.AddColumn<DateTimeOffset>(
name: "creation",
table: "t_detected_operation",
type: "timestamp with time zone",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
comment: "дата создания");
migrationBuilder.AddColumn<int>(
name: "id_editor",
table: "t_detected_operation",
type: "integer",
nullable: true,
comment: "Редактор");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "creation",
table: "t_detected_operation");
migrationBuilder.DropColumn(
name: "id_editor",
table: "t_detected_operation");
migrationBuilder.AlterColumn<int>(
name: "id_user",
table: "t_detected_operation",
type: "integer",
nullable: false,
defaultValue: 0,
comment: "Id пользователя по телеметрии на момент начала операции",
oldClrType: typeof(int),
oldType: "integer",
oldNullable: true,
oldComment: "Id пользователя по телеметрии на момент начала операции");
}
}
}

View File

@ -497,6 +497,11 @@ namespace AsbCloudDb.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("Creation")
.HasColumnType("timestamp with time zone")
.HasColumnName("creation")
.HasComment("дата создания");
b.Property<DateTimeOffset>("DateEnd")
.HasColumnType("timestamp with time zone")
.HasColumnName("date_end")
@ -533,11 +538,16 @@ namespace AsbCloudDb.Migrations
.HasColumnName("id_category")
.HasComment("Id категории операции");
b.Property<int?>("IdEditor")
.HasColumnType("integer")
.HasColumnName("id_editor")
.HasComment("Редактор");
b.Property<int>("IdTelemetry")
.HasColumnType("integer")
.HasColumnName("id_telemetry");
b.Property<int>("IdUsersAtStart")
b.Property<int?>("IdUsersAtStart")
.HasColumnType("integer")
.HasColumnName("id_user")
.HasComment("Id пользователя по телеметрии на момент начала операции");

View File

@ -19,6 +19,12 @@ namespace AsbCloudDb.Model
[Column("id_category"), Comment("Id категории операции")]
public int IdCategory { get; set; }
[Column("id_editor"), Comment("Редактор")]
public int? IdEditor { get; set; }
[Column("creation"), Comment("дата создания")]
public DateTimeOffset Creation { get; set; }
[Column("date_start", TypeName = "timestamp with time zone"), Comment("Дата начала операции")]
public DateTimeOffset DateStart { get; set; }
@ -27,7 +33,7 @@ namespace AsbCloudDb.Model
public DateTimeOffset DateEnd { get; set; }
[Column("id_user"), Comment("Id пользователя по телеметрии на момент начала операции")]
public int IdUsersAtStart { get; set; }
public int? IdUsersAtStart { get; set; }
[NotMapped]
public double DurationMinutes => (DateEnd - DateStart).TotalMinutes;

View File

@ -151,8 +151,6 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
private static DataSaubStatDto CalcStat(DetectedOperationDto operation, Span<TelemetryDataSaubDto> span)
{
var hasOscillation = EnabledSubsystemsFlags.AutoOscillation.HasEnabledSubsystems(operation.EnabledSubsystems);
var aggregatedValues = CalcAggregate(span);
var dateStart = span[0].DateTime;
var dateEnd = span[^1].DateTime;
@ -181,7 +179,7 @@ namespace AsbCloudInfrastructure.Background.PeriodicWorks
RotorSpeed = aggregatedValues.RotorSpeed,
IdCategory = operation.IdCategory,
EnabledSubsystems = operation.EnabledSubsystems,
HasOscillation = hasOscillation,
HasOscillation = operation.EnabledSubsystems.IsAutoOscillation,
IdTelemetry = operation.IdTelemetry,
Flow = aggregatedValues.Flow
};

View File

@ -42,6 +42,7 @@ using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudInfrastructure.Services.ProcessMapPlan.Export;
using AsbCloudInfrastructure.Services.WellOperations.Factories;
@ -51,6 +52,10 @@ namespace AsbCloudInfrastructure
{
public static void MapsterSetup()
{
TypeAdapterConfig.GlobalSettings.Default.Config
.ForType<DetectedOperationDto, DetectedOperation>()
.Ignore(source => source.OperationCategory);
TypeAdapterConfig.GlobalSettings.Default.Config
.ForType<ScheduleDto, Schedule>()
.Ignore(source => source.Driller);

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Exceptions;
using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace AsbCloudInfrastructure.Repository
@ -120,15 +121,26 @@ namespace AsbCloudInfrastructure.Repository
{
if (!dtos.Any())
return 0;
var ids = dtos.Select(d => d.Id);
var countExistingEntities = await dbSet
.Where(d => ids.Contains(d.Id))
var ids = dtos
.Select(o => o.Id)
.Distinct()
.ToArray();
if (ids.Any(id => id == default))
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь Id");
if (ids.Length != dtos.Count())
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь уникальные Id");
var dbSet = dbContext.Set<TEntity>();
var existingEntitiesCount = await dbSet
.Where(o => ids.Contains(o.Id))
.CountAsync(token);
if (ids.Count() > countExistingEntities)
return ICrudRepository<TDto>.ErrorIdNotFound;
if (ids.Length != existingEntitiesCount)
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны существовать в БД");
var entities = dtos.Select(Convert);
var entries = entities.Select(entity => dbSet.Update(entity)).Cast<EntityEntry>().ToList();
@ -145,9 +157,8 @@ namespace AsbCloudInfrastructure.Repository
.FirstOrDefault(e => e.Id == id);
if (entity == default)
return Task.FromResult(ICrudRepository<TDto>.ErrorIdNotFound);
var entry = dbSet.Remove(entity);
dbSet.Remove(entity);
var affected = dbContext.SaveChangesAsync(token);
entry.State = EntityState.Detached;
return affected;
}
@ -164,10 +175,8 @@ namespace AsbCloudInfrastructure.Repository
return ICrudRepository<TDto>.ErrorIdNotFound;
var entities = dbContext.Set<TEntity>().Where(e => ids.Contains(e.Id));
var entries = entities.Select(entity => dbSet.Remove(entity)).Cast<EntityEntry>().ToList();
var affected = await dbContext.SaveChangesAsync(token);
entries.ForEach(e => e.State = EntityState.Detached);
return affected;
dbContext.Set<TEntity>().RemoveRange(entities);
return await dbContext.SaveChangesAsync(token);
}
protected virtual TDto Convert(TEntity src) => src.Adapt<TDto>();

View File

@ -64,10 +64,12 @@ public class DailyReportRepository : CrudRepositoryBase<DailyReportDto, DailyRep
.AsNoTracking()
.SingleOrDefaultAsync(d => d.IdWell == idWell && d.Date == date, cancellationToken);
return entity is null ? null : Convert(entity);
var timezoneOffset = wellService.GetTimezone(idWell).Offset;
return entity is null ? null : Convert(entity, timezoneOffset);
}
protected DailyReportDto Convert(DailyReport src, TimeSpan timezoneOffset)
private static DailyReportDto Convert(DailyReport src, TimeSpan timezoneOffset)
{
var dto = new DailyReportDto
{

View File

@ -1,5 +1,4 @@
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
@ -12,11 +11,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
namespace AsbCloudInfrastructure.Repository;
public class DetectedOperationRepository : CrudRepositoryBase<DetectedOperationDto, DetectedOperation>,
IDetectedOperationRepository
public class DetectedOperationRepository : CrudRepositoryBase<DetectedOperationDto, DetectedOperation>, IDetectedOperationRepository
{
private readonly ITelemetryService telemetryService;
@ -26,23 +25,37 @@ public class DetectedOperationRepository : CrudRepositoryBase<DetectedOperationD
{
this.telemetryService = telemetryService;
}
public async Task<int> Delete(int idUser, DetectedOperationByTelemetryRequest request, CancellationToken token)
public async Task<int> DeleteAsync(DetectedOperationByTelemetryRequest request, CancellationToken token)
{
var query = BuildQuery(request);
dbContext.Set<DetectedOperation>().RemoveRange(query);
return await dbContext.SaveChangesAsync(token);
}
public async Task<int> DeleteRange(int idUser, IEnumerable<int> ids, CancellationToken token)
public async Task<PaginationContainer<DetectedOperationDto>> GetPageAsync(DetectedOperationByTelemetryRequest request, CancellationToken token)
{
var query = dbContext.Set<DetectedOperation>()
.Where(e => ids.Contains( e.Id));
var skip = request.Skip ?? 0;
var take = request.Take ?? 32;
dbContext.Set<DetectedOperation>()
.RemoveRange(query);
var query = BuildQuery(request);
return await dbContext.SaveChangesAsync(token);
var entities = await query.Skip(skip)
.Take(take)
.AsNoTracking()
.ToArrayAsync(token);
var offset = telemetryService.GetTimezone(request.IdTelemetry).Offset;
var paginationContainer = new PaginationContainer<DetectedOperationDto>
{
Skip = skip,
Take = take,
Count = await query.CountAsync(token),
Items = entities.Select(o => Convert(o, offset))
};
return paginationContainer;
}
public async Task<IDictionary<int, DateTimeOffset>> GetLastDetectedDatesAsync(CancellationToken token) =>
@ -59,88 +72,18 @@ public class DetectedOperationRepository : CrudRepositoryBase<DetectedOperationD
{
var query = BuildQuery(request)
.Include(o => o.OperationCategory);
var entities = await query.ToArrayAsync(token);
var entities = await query.AsNoTracking().ToArrayAsync(token);
var offset = telemetryService.GetTimezone(request.IdTelemetry).Offset;
var dtos = entities.Select(o => Convert(o, offset));
return dtos;
}
public async Task<int> Insert(int? idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
if(!dtos.Any())
return 0;
var entities = dtos.Select(Convert);
var dbset = dbContext.Set<DetectedOperation>();
foreach(var entity in entities)
{
entity.Id = default;
dbset.Add(entity);
}
return await dbContext.SaveChangesWithExceptionHandling(token);
}
public async Task<int> Update(int idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
if (!dtos.Any())
return 0;
var ids = dtos
.Select(o => o.Id)
.Distinct()
.ToArray();
if (ids.Any(id => id == default))
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь Id");
if (ids.Length != dtos.Count())
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны иметь уникальные Id");
var dbSet = dbContext.Set<DetectedOperation>();
var existingEntitiesCount = await dbSet
.Where(o => ids.Contains(o.Id))
.CountAsync(token);
if (ids.Length != existingEntitiesCount)
throw new ArgumentInvalidException(nameof(dtos), "Все записи должны существовать в БД");
var entities = dtos
.Select(Convert)
.ToArray();
var entries = new Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<DetectedOperation>[entities.Length];
for(var i = 0; i < entities.Length; i++)
entries[i] = dbSet.Update(entities[i]);
var result = await dbContext.SaveChangesWithExceptionHandling(token);
for (var i = 0; i < entries.Length; i++)
entries[i].State = EntityState.Detached;
return result;
}
public async Task<int> UpdateOrInsert(int idUser, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
var result = 0;
var itemsToInsert = dtos.Where(e => e.Id == 0);
if (itemsToInsert.Any())
result += await Insert(idUser, itemsToInsert, token);
var itemsToUpdate = dtos.Where(e => e.Id != 0);
if (itemsToUpdate.Any())
result += await Update(idUser, itemsToUpdate, token);
return result;
}
private IQueryable<DetectedOperation> BuildQuery(DetectedOperationByTelemetryRequest request)
{
var query = dbContext.Set<DetectedOperation>()
.OrderBy(o => o.DateStart)
.ThenBy(o => o.DepthStart)
.Where(o => o.IdTelemetry == request.IdTelemetry);
if (request.IdsCategories.Any())
@ -165,28 +108,17 @@ public class DetectedOperationRepository : CrudRepositoryBase<DetectedOperationD
}
if (request.SortFields?.Any() == true)
{
query = query.SortBy(request.SortFields);
}
else
query = query
.OrderBy(o => o.DateStart)
.ThenBy(o => o.DepthStart);
if (request.Skip.HasValue)
query = query.Skip((int)request.Skip);
if (request.Take.HasValue)
query = query.Take((int)request.Take);
return query;
}
protected virtual DetectedOperationDto Convert(DetectedOperation src, TimeSpan offset)
private static DetectedOperationDto Convert(DetectedOperation src, TimeSpan offset)
{
var dto = src.Adapt<DetectedOperationDto>();
dto.DateStart = src.DateStart.ToOffset(offset);
dto.DateEnd = src.DateEnd.ToOffset(offset);
dto.EnabledSubsystems = src.EnabledSubsystems;
return dto;
}
@ -197,4 +129,4 @@ public class DetectedOperationRepository : CrudRepositoryBase<DetectedOperationD
entity.DateEnd = src.DateEnd.ToUniversalTime();
return entity;
}
}
}

View File

@ -185,11 +185,11 @@ public class DailyReportExportService : IDailyReportExportService
sheet.Cell(rowСurrent, columnSubsystemName).SetCellValue(subsystem.Name);
sheet.Cell(rowСurrent, columnUseSubsystemPerDayUsedTimeHours).SetCellValue(subsystem.UsagePerDay?.UsedTimeHours);
sheet.Cell(rowСurrent, columnUseSubsystemPerDaySumDepthInterval).SetCellValue(subsystem.UsagePerDay?.SumDepthInterval);
sheet.Cell(rowСurrent, columnUseSubsystemPerDayKUsage).SetCellValue(subsystem.UsagePerDay?.KUsage);
sheet.Cell(rowСurrent, columnUseSubsystemPerDayKUsage).SetCellValue(subsystem.UsagePerDay?.KUsage * 100);
sheet.Cell(rowСurrent, columnUseSubsystemPerWellUsedTimeHours).SetCellValue(subsystem.UsagePerWell?.UsedTimeHours);
sheet.Cell(rowСurrent, columnUseSubsystemPerWellSumDepthInterval).SetCellValue(subsystem.UsagePerWell?.SumDepthInterval);
sheet.Cell(rowСurrent, columnUseSubsystemPerWellKUsage).SetCellValue(subsystem.UsagePerWell?.KUsage);
sheet.Cell(rowСurrent, columnUseSubsystemPerWellKUsage).SetCellValue(subsystem.UsagePerWell?.KUsage * 100);
rowСurrent++;
}

View File

@ -162,8 +162,7 @@ public class DetectedOperationExportService
private static string GetCategoryName(IEnumerable<WellOperationCategoryDto> wellOperationCategories, DetectedOperationDto current)
{
var idCategory = current.IdCategory;
if (idCategory == WellOperationCategory.IdSlide &&
EnabledSubsystemsFlags.AutoOscillation.HasEnabledSubsystems(current.EnabledSubsystems))
if (idCategory == WellOperationCategory.IdSlide && current.EnabledSubsystems.IsAutoOscillation)
return "Бурение в слайде с осцилляцией";
var category = wellOperationCategories.FirstOrDefault(o => o.Id == current.IdCategory);

View File

@ -11,6 +11,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Exceptions;
using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
namespace AsbCloudInfrastructure.Services.DetectOperations;
@ -80,6 +81,43 @@ public class DetectedOperationService : IDetectedOperationService
return dtos;
}
public async Task<int> InsertRangeManualAsync(int idEditor, int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
var idTelemetry = await GetIdTelemetryByWell(idWell, token);
foreach (var dto in dtos)
{
dto.IdEditor = idEditor;
dto.IdTelemetry = idTelemetry;
}
return await operationRepository.InsertRangeAsync(dtos, token);
}
public async Task<int> UpdateRangeManualAsync(int idEditor, int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
var idTelemetry = await GetIdTelemetryByWell(idWell, token);
foreach (var dto in dtos)
{
dto.IdEditor = idEditor;
dto.IdTelemetry = idTelemetry;
}
return await operationRepository.UpdateRangeAsync(dtos, token);
}
private async Task<int> GetIdTelemetryByWell(int idWell, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(idWell, token) ??
throw new ArgumentInvalidException(nameof(idWell), "Well doesn`t exist");
var idTelemetry = well.IdTelemetry ??
throw new ArgumentInvalidException(nameof(idWell), "У скважины отсутствует телеметрия");
return idTelemetry;
}
public async Task<IEnumerable<WellOperationCategoryDto>> GetCategoriesAsync(int? idWell, CancellationToken token)
{
if(idWell is null)
@ -213,7 +251,7 @@ public class DetectedOperationService : IDetectedOperationService
return 0;
var requestByTelemetry = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
var result = await operationRepository.Delete(-1, requestByTelemetry, token);
var result = await operationRepository.DeleteAsync(requestByTelemetry, token);
return result;
}

View File

@ -18,10 +18,10 @@ namespace AsbCloudInfrastructure.Services
private readonly Dictionary<int, string> feedRegulatorData = new ()
{
{ LimitingParameterDto.NoLimit, "Нет ограничения" },
{ LimitingParameterDto.RopPlan, "МСП" },
{ LimitingParameterDto.RopPlan, "Скорость блока" },
{ LimitingParameterDto.Pressure, "Давление" },
{ LimitingParameterDto.AxialLoad, "Осевая нагрузка" },
{ LimitingParameterDto.RotorTorque, "Момент" }
{ LimitingParameterDto.RotorTorque, "Момент на роторе" }
};
public LimitingParameterService(ILimitingParameterRepository limitingParameterRepository,

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@ -26,7 +27,11 @@ public abstract class ProcessMapPlanExportService<TDto> : ExportExcelService<TDt
protected override async Task<IEnumerable<TDto>> GetDtosAsync(WellRelatedExportRequest options, CancellationToken token)
{
var request = new ProcessMapPlanBaseRequestWithWell(options.IdWell);
var request = new ProcessMapPlanBaseRequestWithWell(options.IdWell)
{
Moment = DateTimeOffset.UtcNow
};
var dtos = await processMapPlanRepository.Get(request, token);
return dtos;
}

View File

@ -148,17 +148,18 @@ namespace AsbCloudInfrastructure.Services.SAUB
{
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null;
var from = cacheItem.FirstByDate?.DateTime;
if (!cacheItem.LastData.Any())
return null;
var from = cacheItem.FirstByDate.DateTime;
var to = cacheItem.LastData[^1].DateTime;
from = from ?? cacheItem.LastData[0].DateTime;
return new DatesRangeDto {
From = from.Value.ToUtcDateTimeOffset(cacheItem.TimezoneHours),
To = to.ToUtcDateTimeOffset(cacheItem.TimezoneHours) };
return new DatesRangeDto
{
From = new DateTimeOffset(from, TimeSpan.FromHours(cacheItem.TimezoneHours)),
To = new DateTimeOffset(to, TimeSpan.FromHours(cacheItem.TimezoneHours))
};
}
public DatesRangeDto? GetOrDefaultCachedDateRange(int idTelemetry)

View File

@ -71,7 +71,7 @@ namespace AsbCloudInfrastructure.Services
return DateTimeOffset.MinValue;
var datesRange = telemetryService.GetDatesRange(well.IdTelemetry.Value);
return datesRange.To.DateTime;
return datesRange.To;
}
/// <inheritdoc/>

View File

@ -0,0 +1,23 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Requests;
using Refit;
namespace AsbCloudWebApi.IntegrationTests.Clients;
public interface IDetectedOperationClient
{
private const string BaseRoute = "/api/well/{idWell}/DetectedOperation";
[Post(BaseRoute)]
Task<IApiResponse<int>> InsertRangeAsync(int idWell, IEnumerable<DetectedOperationDto> dtos);
[Put(BaseRoute)]
Task<IApiResponse<int>> UpdateRangeAsync(int idWell, IEnumerable<DetectedOperationDto> dtos);
[Delete(BaseRoute)]
Task<IApiResponse<int>> DeleteRangeAsync(int idWell, [Body] IEnumerable<int> ids);
[Get(BaseRoute)]
Task<IApiResponse<PaginationContainer<DetectedOperationDto>>> GetAsync(int idWell, [Query] DetectedOperationRequest request);
}

View File

@ -19,7 +19,7 @@ public interface IWellOperationClient
Task<IApiResponse<int>> UpdateRangeAsync(int idWell, [Body] IEnumerable<WellOperationDto> dtos);
[Get(BaseRoute)]
Task<IApiResponse<PaginationContainer<WellOperationDto>>> GetPageOperationsPlanAsync(int idWell, [Query] WellOperationRequestBase request);
Task<IApiResponse<PaginationContainer<WellOperationDto>>> GetPageOperationsAsync(int idWell, [Query] WellOperationRequestBase request);
[Multipart]
[Post(BaseRoute + "/parse/{idType}")]

View File

@ -0,0 +1,182 @@
using System.Net;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Requests;
using AsbCloudDb.Model;
using AsbCloudWebApi.IntegrationTests.Clients;
using AsbCloudWebApi.IntegrationTests.Data;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Xunit;
namespace AsbCloudWebApi.IntegrationTests.Controllers;
public class DetectedOperationControllerTests : BaseIntegrationTest
{
private readonly IDetectedOperationClient client;
private readonly DetectedOperationDto dto = new()
{
IdCategory = WellOperationCategory.IdRotor,
DateStart = new DateTimeOffset(new DateTime(2023, 5, 12, 1,0,0, DateTimeKind.Utc)),
DateEnd = new DateTimeOffset(new DateTime(2023, 5, 12, 1,0,0, DateTimeKind.Utc)),
DepthStart = 0,
DepthEnd = 80,
OperationCategory = new WellOperationCategoryDto
{
Id = WellOperationCategory.IdRotor,
IdParent = WellOperationCategory.IdDrilling,
Name = "Бурение ротором"
},
EnabledSubsystems = new EnabledSubsystems
{
IsAutoRotor = true
},
Value = 400,
};
public DetectedOperationControllerTests(WebAppFactoryFixture factory)
: base(factory)
{
client = factory.GetAuthorizedHttpClient<IDetectedOperationClient>(string.Empty);
dbContext.CleanupDbSet<DetectedOperation>();
}
[Fact]
public async Task InsertRangeAsync_returns_success()
{
//arrange
var well = dbContext.Wells.First();
dto.IdTelemetry = well.IdTelemetry!.Value;
//act
var response = await client.InsertRangeAsync(well.Id, new[] { dto });
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.Equal(1, response.Content);
}
[Fact]
public async Task UpdateRangeAsync_returns_success()
{
//arrange
var well = dbContext.Wells.First();
dto.IdTelemetry = well.IdTelemetry!.Value;
var entity = dto.Adapt<DetectedOperation>();
dbContext.DetectedOperations.Add(entity);
await dbContext.SaveChangesAsync();
dto.Id = entity.Id;
//act
var response = await client.UpdateRangeAsync(well.Id, new[] { dto });
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.Equal(1, response.Content);
}
[Fact]
public async Task UpdateRangeAsync_returns_bad_request_when_id_is_invalid()
{
//arrange
var well = dbContext.Wells.First();
//act
var response = await client.UpdateRangeAsync(well.Id, new[] { dto });
//assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task DeleteRangeAsync_returns_success()
{
//arrange
var well = dbContext.Wells.First();
dto.IdTelemetry = well.IdTelemetry!.Value;
var entity = dto.Adapt<DetectedOperation>();
dbContext.DetectedOperations.Add(entity);
await dbContext.SaveChangesAsync();
var ids = await dbContext.DetectedOperations.Select(d => d.Id).ToArrayAsync();
//act
var response = await client.DeleteRangeAsync(well.Id, ids);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.Equal(1, response.Content);
}
[Fact]
public async Task GetAsync_returns_first_page()
{
//arrange
const int pageSize = 10;
const int pageIndex = 0;
var request = new DetectedOperationRequest
{
Skip = pageIndex,
Take = pageSize,
IdsCategories = new[] { dto.IdCategory }
};
var well = dbContext.Wells.First();
dto.IdTelemetry = well.IdTelemetry!.Value;
var entity = dto.Adapt<DetectedOperation>();
dbContext.DetectedOperations.Add(entity);
await dbContext.SaveChangesAsync();
//act
var response = await client.GetAsync(well.Id, request);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
var totalExpected = response.Content.Count - pageSize * pageIndex;
Assert.Equal(totalExpected, response.Content.Items.Count());
}
[Fact]
public async Task GetAsync_returns_first_operation()
{
//arrange
var well = dbContext.Wells.First();
dto.IdTelemetry = well.IdTelemetry!.Value;
var entity = dto.Adapt<DetectedOperation>();
dbContext.DetectedOperations.Add(entity);
await dbContext.SaveChangesAsync();
var request = new DetectedOperationRequest
{
IdsCategories = new[] { dto.IdCategory }
};
//act
var response = await client.GetAsync(well.Id, request);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
var firstOperation = response.Content.Items.ElementAt(0);
Assert.Equal(well.IdTelemetry, firstOperation.IdTelemetry);
Assert.Equal(dto.EnabledSubsystems, firstOperation.EnabledSubsystems);
Assert.Equal(dto.DateStart.ToOffset(TimeSpan.FromHours(Defaults.Timezone.Hours)), firstOperation.DateStart);
Assert.Equal(dto.DateEnd.ToOffset(TimeSpan.FromHours(Defaults.Timezone.Hours)), firstOperation.DateEnd);
}
}

View File

@ -89,9 +89,12 @@ public class WellOperationControllerTest : BaseIntegrationTest
/// </summary>
/// <returns></returns>
[Fact]
public async Task GetPageOperationsPlanAsync_returns_success()
public async Task GetPageOperationsAsync_returns_first_page()
{
//arrange
const int pageSize = 10;
const int pageIndex = 0;
var well = await dbContext.Wells.FirstAsync();
var entity = CreateWellOperation(well.Id);
dbContext.WellOperations.Add(entity);
@ -104,17 +107,22 @@ public class WellOperationControllerTest : BaseIntegrationTest
var request = new WellOperationRequestBase
{
OperationType = WellOperation.IdOperationTypePlan
OperationType = WellOperation.IdOperationTypePlan,
Skip = pageIndex,
Take = pageSize,
};
//act
var response = await client.GetPageOperationsPlanAsync(well.Id, request);
var response = await client.GetPageOperationsAsync(well.Id, request);
//assert
Assert.Equal(response.StatusCode, HttpStatusCode.OK);
Assert.NotNull(response.Content);
Assert.Single(response.Content.Items);
var totalExpected = response.Content.Count - pageSize * pageIndex;
Assert.Equal(totalExpected, response.Content.Items.Count());
Assert.Single(response.Content.Items);
var actualDto = response.Content.Items.First();
MatchHelper.Match(dto, actualDto);

View File

@ -0,0 +1,42 @@
using AsbCloudApp.Data.DetectedOperation;
using Xunit;
namespace AsbCloudWebApi.Tests.Services.DetectedOperations;
public class EnabledSubsystemsTests
{
[Fact]
public void Create_enable_subsystem_with_the_systems_turned_off()
{
//act
EnabledSubsystems enableSubsystem = 0;
//arrange
Assert.False(enableSubsystem.IsAutoRotor);
Assert.False(enableSubsystem.IsAutoSlide);
Assert.False(enableSubsystem.IsAutoConditionig);
Assert.False(enableSubsystem.IsAutoSinking);
Assert.False(enableSubsystem.IsAutoLifting);
Assert.False(enableSubsystem.IsAutoLiftingWithConditionig);
Assert.False(enableSubsystem.IsAutoBlocknig);
Assert.False(enableSubsystem.IsAutoOscillation);
}
[Fact]
public void Create_enable_subsystem_with_the_auto_slide_subsystem()
{
//act
EnabledSubsystems enableSubsystem = 2;
//arrange
Assert.True(enableSubsystem.IsAutoSlide);
Assert.False(enableSubsystem.IsAutoRotor);
Assert.False(enableSubsystem.IsAutoConditionig);
Assert.False(enableSubsystem.IsAutoSinking);
Assert.False(enableSubsystem.IsAutoLifting);
Assert.False(enableSubsystem.IsAutoLiftingWithConditionig);
Assert.False(enableSubsystem.IsAutoBlocknig);
Assert.False(enableSubsystem.IsAutoOscillation);
}
}

View File

@ -6,138 +6,192 @@ using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudInfrastructure.Services.DetectOperations;
using Microsoft.AspNetCore.Http;
namespace AsbCloudWebApi.Controllers.SAUB
{
/// <summary>
/// Операции определенные по телеметрии САУБ
/// </summary>
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class DetectedOperationController : ControllerBase
{
private readonly IDetectedOperationService detectedOperationService;
private readonly IWellService wellService;
private readonly DetectedOperationExportService detectedOperationExportService;
public DetectedOperationController(IDetectedOperationService detectedOperationService, IWellService wellService,
DetectedOperationExportService detectedOperationExportService)
{
this.detectedOperationService = detectedOperationService;
this.wellService = wellService;
this.detectedOperationExportService = detectedOperationExportService;
}
/// <summary>
/// Операции определенные по телеметрии САУБ
/// </summary>
[Route("api/well/{idWell}/[controller]")]
[ApiController]
[Authorize]
public class DetectedOperationController : ControllerBase
{
private readonly IDetectedOperationRepository detectedOperationRepository;
private readonly IDetectedOperationService detectedOperationService;
private readonly IWellService wellService;
private readonly DetectedOperationExportService detectedOperationExportService;
/// <summary>
/// получить справочник операций. Отличается от операций заводимых вручную.
/// При задании id скважины вернет только те операции, которые определились в телеметрии этой скважины.
/// </summary>
/// <param name="idWell">[опционально] id скважины</param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("categories")]
[ProducesResponseType(typeof(IEnumerable<WellOperationCategoryDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetCategoriesAsync([FromQuery] int? idWell, CancellationToken token)
{
var result = await detectedOperationService.GetCategoriesAsync(idWell, token);
return Ok(result);
}
public DetectedOperationController(IDetectedOperationService detectedOperationService,
IWellService wellService,
DetectedOperationExportService detectedOperationExportService,
IDetectedOperationRepository detectedOperationRepository)
{
this.detectedOperationService = detectedOperationService;
this.wellService = wellService;
this.detectedOperationExportService = detectedOperationExportService;
this.detectedOperationRepository = detectedOperationRepository;
}
/// <summary>
/// Получить фильтрованный список операций по телеметрии САУБ
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(typeof(DetectedOperationListDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetAsync(
[FromQuery] DetectedOperationByWellRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid();
/// <summary>
/// Добавить операции
/// </summary>
/// <param name="idWell"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
public async Task<IActionResult> InsertRangeAsync(int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
var idUser = await AssertUserHasAccessToWellAsync(idWell, token);
var result = await detectedOperationService.GetAsync(request, token);
return Ok(result);
}
var result = await detectedOperationService.InsertRangeManualAsync(idUser, idWell, dtos, token);
/// <summary>
/// Получить статистику по фильтрованному списку операций по телеметрии САУБ
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("stat")]
[ProducesResponseType(typeof(IEnumerable<DetectedOperationStatDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetStatAsync(
[FromQuery] DetectedOperationByWellRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid();
return Ok(result);
}
var result = await detectedOperationService.GetOperationsStatAsync(request, token);
return Ok(result);
}
/// <summary>
/// Обновить операции
/// </summary>
/// <param name="idWell"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPut]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
public async Task<IActionResult> UpdateRangeAsync(int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
{
var idUser = await AssertUserHasAccessToWellAsync(idWell, token);
/// <summary>
/// Удалить операции.
/// Удаленные операции будут определены повторно сервисом автоматизированного определения операций.
/// Может потребоваться при изменении алгоритмов определения
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpDelete]
[Permission]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> DeleteAsync(
[FromQuery] DetectedOperationByWellRequest request,
CancellationToken token)
{
if (!await UserHasAccessToWellAsync(request.IdWell, token))
return Forbid();
var result = await detectedOperationService.UpdateRangeManualAsync(idUser, idWell, dtos, token);
var result = await detectedOperationService.DeleteAsync(request, token);
return Ok(result);
}
return Ok(result);
}
protected async Task<bool> UserHasAccessToWellAsync(int idWell, CancellationToken token)
{
var idCompany = User.GetCompanyId();
if (idCompany is not null &&
await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, idWell, token)
.ConfigureAwait(false))
return true;
return false;
}
/// <summary>
/// Удалить операции
/// </summary>
/// <param name="idWell"></param>
/// <param name="ids"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpDelete]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
public async Task<IActionResult> DeleteRangeAsync(int idWell, IEnumerable<int> ids, CancellationToken token)
{
await AssertUserHasAccessToWellAsync(idWell, token);
/// <summary>
/// Создает excel файл с операциями по скважине
/// </summary>
/// <param name="idWell">id скважины</param>
/// <param name="token"></param>
[HttpGet("export")]
[Permission]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public async Task<IActionResult> ExportAsync(int idWell, CancellationToken token)
{
var idCompany = User.GetCompanyId();
var result = await detectedOperationRepository.DeleteRangeAsync(ids, token);
if (idCompany is null)
return Forbid();
return Ok(result);
}
var host = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}";
var stream = await detectedOperationExportService.ExportAsync(idWell, host, token);
return File(stream, "application/octet-stream", "operations.xlsx");
}
}
}
/// <summary>
/// получить справочник операций. Отличается от операций заводимых вручную.
/// При задании id скважины вернет только те операции, которые определились в телеметрии этой скважины.
/// </summary>
/// <param name="idWell">[опционально] id скважины</param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet]
[Route("/api/well/[controller]/categories")]
[ProducesResponseType(typeof(IEnumerable<WellOperationCategoryDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetCategoriesAsync([FromQuery] int? idWell, CancellationToken token)
{
var result = await detectedOperationService.GetCategoriesAsync(idWell, token);
return Ok(result);
}
/// <summary>
/// Получить фильтрованный список операций по телеметрии САУБ
/// </summary>
/// <param name="idWell"></param>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(typeof(PaginationContainer<DetectedOperationDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAsync(int idWell, [FromQuery] DetectedOperationRequest request,
CancellationToken token)
{
await AssertUserHasAccessToWellAsync(idWell, token);
var well = await wellService.GetOrDefaultAsync(idWell, token);
if (well?.IdTelemetry is null)
return NoContent();
var requestToService = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);
var result = await detectedOperationRepository.GetPageAsync(requestToService, token);
return Ok(result);
}
/// <summary>
/// Получить статистику по фильтрованному списку операций по телеметрии САУБ
/// </summary>
/// <param name="idWell"></param>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("stat")]
[ProducesResponseType(typeof(IEnumerable<DetectedOperationStatDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetStatAsync(int idWell, [FromQuery] DetectedOperationRequest request,
CancellationToken token)
{
await AssertUserHasAccessToWellAsync(idWell, token);
var requestToService = new DetectedOperationByWellRequest(idWell, request);
var result = await detectedOperationService.GetOperationsStatAsync(requestToService, token);
return Ok(result);
}
/// <summary>
/// Создает excel файл с операциями по скважине
/// </summary>
/// <param name="idWell">id скважины</param>
/// <param name="token"></param>
[HttpGet("export")]
[Permission]
[ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public async Task<IActionResult> ExportAsync(int idWell, CancellationToken token)
{
var idCompany = User.GetCompanyId();
if (idCompany is null)
return Forbid();
var host = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}";
var stream = await detectedOperationExportService.ExportAsync(idWell, host, token);
return File(stream, "application/octet-stream", "operations.xlsx");
}
private async Task<int> AssertUserHasAccessToWellAsync(int idWell, CancellationToken token)
{
var idUser = User.GetUserId();
var idCompany = User.GetCompanyId();
if (!idUser.HasValue)
throw new ForbidException("Неизвестный пользователь");
if (!idCompany.HasValue)
throw new ForbidException("Нет доступа к скважине");
if (!await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, token))
throw new ForbidException("Нет доступа к скважине");
return idUser.Value;
}
}
}