Merge branch 'dev' into feature/sections

# Conflicts:
#	AsbCloudDb/Migrations/AsbCloudDbContextModelSnapshot.cs
#	AsbCloudDb/Model/AsbCloudDbContext.cs
#	AsbCloudDb/Model/DefaultData/EntityFillerPermission.cs
#	AsbCloudDb/Model/IAsbCloudDbContext.cs
This commit is contained in:
Степанов Дмитрий 2023-12-04 17:22:29 +05:00
commit f8117db6cf
64 changed files with 29082 additions and 980 deletions

View File

@ -1,4 +1,4 @@
namespace AsbCloudApp.Data
namespace AsbCloudApp.Data.Trajectory
{

View File

@ -1,7 +1,7 @@
namespace AsbCloudApp.Data
namespace AsbCloudApp.Data.Trajectory
{
/// <summary>
/// Визуализация траектории 3D для посторения радиуса цели
/// Визуализация траектории 3D для построения радиуса цели
/// </summary>
public class TrajectoryCartesianPlanDto : TrajectoryCartesianFactDto
{

View File

@ -0,0 +1,53 @@
using System;
namespace AsbCloudApp.Data.Trajectory
{
/// <summary>
/// Базовая географическая траектория
/// </summary>
public abstract class TrajectoryGeoDto
{
/// <summary>
/// ИД строки с координатами
/// </summary>
public int Id { get; set; }
/// <summary>
/// Id скважины
/// </summary>
public int IdWell { get; set; }
/// <summary>
/// Глубина по стволу
/// </summary>
public double WellboreDepth { get; set; }
/// <summary>
/// Угол зенитный
/// </summary>
public double ZenithAngle { get; set; }
/// <summary>
/// Азимут Географ.
/// </summary>
public double AzimuthGeo { get; set; }
/// <summary>
/// Азимут Магнитный
/// </summary>
public double? AzimuthMagnetic { get; set; }
/// <summary>
/// Глубина вертикальная
/// </summary>
public double? VerticalDepth { get; set; }
/// <summary>
/// Дата загрузки
/// </summary>
public DateTime UpdateDate { get; set; }
/// <summary>
/// ИД пользователя
/// </summary>
public int IdUser { get; set; }
}
}

View File

@ -0,0 +1,15 @@
namespace AsbCloudApp.Data.Trajectory;
/// <summary>
/// Формирование данных по фактической географической траектории
/// </summary>
public class TrajectoryGeoFactDto : TrajectoryGeoDto
{
/// <summary>
/// Комментарии
/// </summary>
public string? Comment { get; set; }
}

View File

@ -0,0 +1,21 @@
using System;
namespace AsbCloudApp.Data.Trajectory
{
/// <summary>
/// Формирование данных по плановой географической траектории
/// </summary>
public class TrajectoryGeoPlanDto : TrajectoryGeoDto
{
/// <summary>
/// Радиус цели
/// </summary>
public double? Radius { get; set; }
/// <summary>
/// Комментарии
/// </summary>
public string? Comment { get; set; }
}
}

View File

@ -0,0 +1,25 @@
namespace AsbCloudApp.Data.Trajectory
{
/// <summary>
/// DTO объединяющее плановые и фактические значения траекторий
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="V"></typeparam>
public class TrajectoryPlanFactDto<T, V>
{
/// <summary>
/// Плановое значение
/// </summary>
public T? Plan { get; set; }
/// <summary>
/// Фактическое значение
/// </summary>
public V? FactManual { get; set; }
/// <summary>
/// Фактическое ннб-значение
/// </summary>
public V? FactNnb { get; set; }
}
}

View File

@ -1,45 +0,0 @@
namespace AsbCloudApp.Data;
/// <summary>
/// Базовая географическая траектория
/// </summary>
public abstract class TrajectoryGeoDto
{
/// <summary>
/// Id скважины
/// </summary>
public int IdWell { get; set; }
/// <summary>
/// Глубина по стволу
/// </summary>
public double WellboreDepth { get; set; }
/// <summary>
/// Угол зенитный
/// </summary>
public double ZenithAngle { get; set; }
/// <summary>
/// Азимут Географ.
/// </summary>
public double AzimuthGeo { get; set; }
/// <summary>
/// Азимут Магнитный
/// </summary>
public double? AzimuthMagnetic { get; set; }
/// <summary>
/// Глубина вертикальная
/// </summary>
public double? VerticalDepth { get; set; }
}
/// <summary>
/// Формирование данных по фактической географической траектории
/// </summary>
public class TrajectoryGeoFactDto : TrajectoryGeoDto
{ }

View File

@ -1,35 +0,0 @@
using System;
namespace AsbCloudApp.Data
{
/// <summary>
/// Формирование данных по плановой географической траектории
/// </summary>
public class TrajectoryGeoPlanDto: TrajectoryGeoDto
{
/// <summary>
/// ИД строки с координатами
/// </summary>
public int Id { get; set; }
/// <summary>
/// Дата загрузки
/// </summary>
public DateTime UpdateDate { get; set; }
/// <summary>
/// ИД пользователя
/// </summary>
public int IdUser { get; set; }
/// <summary>
/// Радиус цели
/// </summary>
public double? Radius { get; set; }
/// <summary>
/// Комментарии
/// </summary>
public string? Comment { get; set; }
}
}

View File

@ -1,4 +1,4 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.Trajectory;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@ -6,26 +6,27 @@ using System.Threading.Tasks;
namespace AsbCloudApp.Repositories
{
/// <summary>
/// CRUD для работы с плановой траекторией из клиента
/// CRUD-репозиторий для работы с траекторией из клиента (плановой и фактической)
/// </summary>
/// <returns></returns>
public interface ITrajectoryPlanRepository : ITrajectoryRepository<TrajectoryGeoPlanDto>
//TrajectoryGeoPlanDto
public interface ITrajectoryEditableRepository<T> : ITrajectoryRepository<T> where T : TrajectoryGeoDto
{
/// <summary>
/// Добавить строки с координатами по одной скважине. Если в коллекции координаты для разных скважин получаем exception.
/// </summary>
/// <param name="plannedTrajectoryRows"></param>
/// <param name="trajectoryRows"></param>
/// <param name="token"></param>
/// <returns>количество записанных строк или exception с описанием</returns>
Task<int> AddRangeAsync(IEnumerable<TrajectoryGeoPlanDto> plannedTrajectoryRows, CancellationToken token);
Task<int> AddRangeAsync(IEnumerable<T> trajectoryRows, CancellationToken token);
/// <summary>
/// Добавить одну строку с координатами
/// </summary>
/// <param name="plannedTrajectoryRow"></param>
/// <param name="trajectoryRow"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> AddAsync(TrajectoryGeoPlanDto plannedTrajectoryRow, CancellationToken token);
Task<int> AddAsync(T trajectoryRow, CancellationToken token);
/// <summary>
/// Обновить строку с координатами
@ -33,7 +34,7 @@ namespace AsbCloudApp.Repositories
/// <param name="row"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> UpdateAsync(TrajectoryGeoPlanDto row,
Task<int> UpdateAsync(T row,
CancellationToken token);
/// <summary>
@ -45,7 +46,7 @@ namespace AsbCloudApp.Repositories
Task<int> DeleteRangeAsync(IEnumerable<int> ids, CancellationToken token);
/// <summary>
/// Удалить всю плановую траекторию по ИД скважины
/// Удалить всю траекторию по ИД скважины
/// </summary>
/// <param name="idWell"></param>
/// <param name="token"></param>

View File

@ -1,23 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Requests;
namespace AsbCloudApp.Repositories
{
/// <summary>
/// CRUD для работы с фактической траекторией из клиента
/// </summary>
/// <returns></returns>
public interface ITrajectoryFactRepository : ITrajectoryRepository<TrajectoryGeoFactDto>
{
/// <summary>
/// Получить траектории скважины
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<TrajectoryGeoFactDto>> GetAsync(TrajectoryGeoFactRequest request, CancellationToken token);
}
}

View File

@ -0,0 +1,23 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Requests;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Repositories
{
/// <summary>
/// репозиторий для работы с траекторией из ннб (фактической)
/// </summary>
/// <returns></returns>
public interface ITrajectoryNnbRepository : ITrajectoryRepository<TrajectoryGeoFactDto>
{
/// <summary>
/// получение списка траекторий по параметрам запроса
/// </summary>
/// <param name="trajectoryRequest">параметры запроса</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IEnumerable<TrajectoryGeoFactDto>> GetByRequestAsync(TrajectoryRequest trajectoryRequest, CancellationToken cancellationToken);
}
}

View File

@ -1,8 +1,5 @@
using AsbCloudApp.Data;
using System;
using AsbCloudApp.Data.Trajectory;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -15,9 +12,9 @@ namespace AsbCloudApp.Repositories
where T : TrajectoryGeoDto
{
/// <summary>
/// Получить все добавленные по скважине координаты плановой траектории
/// Получить все добавленные по скважине координаты траектории
/// </summary>
/// <param name="idWell"></param>
/// <param name="idWell">ключ скважины</param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<T>> GetAsync(int idWell, CancellationToken token);

View File

@ -5,7 +5,7 @@ namespace AsbCloudApp.Requests;
/// <summary>
/// Запрос для получения фактической траектории
/// </summary>
public class TrajectoryGeoFactRequest : RequestBase
public class TrajectoryRequest : RequestBase
{
/// <summary>
/// Идентификатор скважины

View File

@ -1,40 +0,0 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Services
{
/// <summary>
/// Сервис загрузки и обработки плановой траектории из файла
/// </summary>
public interface IPlannedTrajectoryImportService
{
/// <summary>
/// скачать шаблон для заполнения плановой траектории
/// </summary>
/// <returns></returns>
Stream GetTemplateFile();
/// <summary>
/// Получить имя файла (исходя из названия скважины)
/// </summary>
/// <returns></returns>
Task<string> GetFileNameAsync(int idWell, CancellationToken token);
/// <summary>
/// загрузить текущую плановую траекторию в .xlsx
/// </summary>
/// <param name="idWell"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<Stream> ExportAsync(int idWell, CancellationToken token);
/// <summary>
/// импортировать из excel плановую траекторию
/// </summary>
/// <param name="idWell"></param>
/// <param name="idUser"></param>
/// <param name="stream"></param>
/// <param name="token"></param>
/// <param name="deleteBeforeImport">Очистить старые координаты перед импортом (если файл проходит валидацию)</param>
Task<int> ImportAsync(int idWell, int idUser, Stream stream, bool deleteBeforeImport, CancellationToken token);
}
}

View File

@ -1,5 +1,5 @@
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperationImport;
namespace AsbCloudApp.Services.WellOperationImport;
@ -16,7 +16,5 @@ public interface IWellOperationImportService
/// <param name="idUser"></param>
/// <param name="idType"></param>
/// <param name="sheet"></param>
/// <param name="deleteBeforeImport"></param>
/// <param name="cancellationToken"></param>
Task ImportAsync(int idWell, int idUser, int idType, SheetDto sheet, bool deleteBeforeImport, CancellationToken cancellationToken);
IEnumerable<WellOperationDto> Import(int idWell, int idUser, int idType, SheetDto sheet);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_Fact_Trajectory_Table : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "t_trajectory_fact",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
id_user = table.Column<int>(type: "integer", nullable: false, comment: "ID пользователя который внес/изменил запись"),
id_well = table.Column<int>(type: "integer", nullable: false, comment: "ID скважины"),
update_date = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата загрузки траектории"),
wellbore_depth = table.Column<double>(type: "double precision", nullable: false, comment: "Глубина по стволу"),
zenith_angle = table.Column<double>(type: "double precision", nullable: false, comment: "Угол зенитный"),
azimuth_geo = table.Column<double>(type: "double precision", nullable: false, comment: "Азимут Географ."),
azimuth_magnetic = table.Column<double>(type: "double precision", nullable: false, comment: "Азимут Магнитный"),
vertical_depth = table.Column<double>(type: "double precision", nullable: false, comment: "Глубина вертикальная"),
comment = table.Column<string>(type: "text", nullable: true, comment: "Комментарии")
},
constraints: table =>
{
table.PrimaryKey("PK_t_trajectory_fact", x => x.id);
table.ForeignKey(
name: "FK_t_trajectory_fact_t_user_id_user",
column: x => x.id_user,
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_t_trajectory_fact_t_well_id_well",
column: x => x.id_well,
principalTable: "t_well",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
},
comment: "Загрузка фактической траектории");
migrationBuilder.CreateIndex(
name: "IX_t_trajectory_fact_id_user",
table: "t_trajectory_fact",
column: "id_user");
migrationBuilder.CreateIndex(
name: "IX_t_trajectory_fact_id_well",
table: "t_trajectory_fact",
column: "id_well");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "t_trajectory_fact");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,109 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Update_t_planned_trajectory : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_t_planned_trajectory_t_user_id_user",
table: "t_planned_trajectory");
migrationBuilder.DropForeignKey(
name: "FK_t_planned_trajectory_t_well_id_well",
table: "t_planned_trajectory");
migrationBuilder.DropPrimaryKey(
name: "PK_t_planned_trajectory",
table: "t_planned_trajectory");
migrationBuilder.RenameTable(
name: "t_planned_trajectory",
newName: "t_trajectory_planned");
migrationBuilder.RenameIndex(
name: "IX_t_planned_trajectory_id_well",
table: "t_trajectory_planned",
newName: "IX_t_trajectory_planned_id_well");
migrationBuilder.RenameIndex(
name: "IX_t_planned_trajectory_id_user",
table: "t_trajectory_planned",
newName: "IX_t_trajectory_planned_id_user");
migrationBuilder.AddPrimaryKey(
name: "PK_t_trajectory_planned",
table: "t_trajectory_planned",
column: "id");
migrationBuilder.AddForeignKey(
name: "FK_t_trajectory_planned_t_user_id_user",
table: "t_trajectory_planned",
column: "id_user",
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_trajectory_planned_t_well_id_well",
table: "t_trajectory_planned",
column: "id_well",
principalTable: "t_well",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_t_trajectory_planned_t_user_id_user",
table: "t_trajectory_planned");
migrationBuilder.DropForeignKey(
name: "FK_t_trajectory_planned_t_well_id_well",
table: "t_trajectory_planned");
migrationBuilder.DropPrimaryKey(
name: "PK_t_trajectory_planned",
table: "t_trajectory_planned");
migrationBuilder.RenameTable(
name: "t_trajectory_planned",
newName: "t_planned_trajectory");
migrationBuilder.RenameIndex(
name: "IX_t_trajectory_planned_id_well",
table: "t_planned_trajectory",
newName: "IX_t_planned_trajectory_id_well");
migrationBuilder.RenameIndex(
name: "IX_t_trajectory_planned_id_user",
table: "t_planned_trajectory",
newName: "IX_t_planned_trajectory_id_user");
migrationBuilder.AddPrimaryKey(
name: "PK_t_planned_trajectory",
table: "t_planned_trajectory",
column: "id");
migrationBuilder.AddForeignKey(
name: "FK_t_planned_trajectory_t_user_id_user",
table: "t_planned_trajectory",
column: "id_user",
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_planned_trajectory_t_well_id_well",
table: "t_planned_trajectory",
column: "id_well",
principalTable: "t_well",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,109 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Rename_t_trajectory_plan : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_t_trajectory_planned_t_user_id_user",
table: "t_trajectory_planned");
migrationBuilder.DropForeignKey(
name: "FK_t_trajectory_planned_t_well_id_well",
table: "t_trajectory_planned");
migrationBuilder.DropPrimaryKey(
name: "PK_t_trajectory_planned",
table: "t_trajectory_planned");
migrationBuilder.RenameTable(
name: "t_trajectory_planned",
newName: "t_trajectory_plan");
migrationBuilder.RenameIndex(
name: "IX_t_trajectory_planned_id_well",
table: "t_trajectory_plan",
newName: "IX_t_trajectory_plan_id_well");
migrationBuilder.RenameIndex(
name: "IX_t_trajectory_planned_id_user",
table: "t_trajectory_plan",
newName: "IX_t_trajectory_plan_id_user");
migrationBuilder.AddPrimaryKey(
name: "PK_t_trajectory_plan",
table: "t_trajectory_plan",
column: "id");
migrationBuilder.AddForeignKey(
name: "FK_t_trajectory_plan_t_user_id_user",
table: "t_trajectory_plan",
column: "id_user",
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_trajectory_plan_t_well_id_well",
table: "t_trajectory_plan",
column: "id_well",
principalTable: "t_well",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_t_trajectory_plan_t_user_id_user",
table: "t_trajectory_plan");
migrationBuilder.DropForeignKey(
name: "FK_t_trajectory_plan_t_well_id_well",
table: "t_trajectory_plan");
migrationBuilder.DropPrimaryKey(
name: "PK_t_trajectory_plan",
table: "t_trajectory_plan");
migrationBuilder.RenameTable(
name: "t_trajectory_plan",
newName: "t_trajectory_planned");
migrationBuilder.RenameIndex(
name: "IX_t_trajectory_plan_id_well",
table: "t_trajectory_planned",
newName: "IX_t_trajectory_planned_id_well");
migrationBuilder.RenameIndex(
name: "IX_t_trajectory_plan_id_user",
table: "t_trajectory_planned",
newName: "IX_t_trajectory_planned_id_user");
migrationBuilder.AddPrimaryKey(
name: "PK_t_trajectory_planned",
table: "t_trajectory_planned",
column: "id");
migrationBuilder.AddForeignKey(
name: "FK_t_trajectory_planned_t_user_id_user",
table: "t_trajectory_planned",
column: "id_user",
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_t_trajectory_planned_t_well_id_well",
table: "t_trajectory_planned",
column: "id_well",
principalTable: "t_well",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@ -264,46 +264,24 @@ namespace AsbCloudDb.Migrations
modelBuilder.Entity("AsbCloudDb.Model.DailyReports.DailyReport", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone")
.HasColumnName("date")
.HasComment("Дата формирования отчёта");
b.Property<DateTime?>("DateLastUpdate")
.HasColumnType("timestamp with time zone")
.HasColumnName("date_last_update")
.HasComment("Дата последнего обновления");
b.Property<int>("IdWell")
.HasColumnType("integer")
.HasColumnName("id_well")
.HasComment("ID скважины");
b.Property<string>("SignBlock")
b.Property<DateOnly>("StartDate")
.HasColumnType("date")
.HasColumnName("start_date")
.HasComment("Дата отчёта");
b.Property<string>("Info")
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("sign_block")
.HasComment("Подпись");
.HasColumnName("info")
.HasComment("Список параметров для отчёта");
b.Property<string>("SubsystemBlock")
.HasColumnType("jsonb")
.HasColumnName("subsystem_block")
.HasComment("Наработкой подсистем");
b.Property<string>("TimeBalanceBlock")
.HasColumnType("jsonb")
.HasColumnName("time_balance_block")
.HasComment("Баланс времени");
b.HasKey("Id");
b.HasIndex("IdWell", "Date")
.IsUnique();
b.HasKey("IdWell", "StartDate")
.HasName("t_id_well_date_start_pk");
b.ToTable("t_daily_report");
@ -2437,12 +2415,6 @@ namespace AsbCloudDb.Migrations
Name = "WellContact.delete"
},
new
{
Id = 529,
Description = "Разрешение на получение отчетов drill test",
Name = "DrillTestReport.get"
},
new
{
Id = 530,
Description = "Разрешение на редактирование плановой конструкции скважины",
@ -2456,76 +2428,6 @@ namespace AsbCloudDb.Migrations
});
});
modelBuilder.Entity("AsbCloudDb.Model.PlannedTrajectory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<double>("AzimuthGeo")
.HasColumnType("double precision")
.HasColumnName("azimuth_geo")
.HasComment("Азимут Географ.");
b.Property<double>("AzimuthMagnetic")
.HasColumnType("double precision")
.HasColumnName("azimuth_magnetic")
.HasComment("Азимут Магнитный");
b.Property<string>("Comment")
.HasColumnType("text")
.HasColumnName("comment")
.HasComment("Комментарии");
b.Property<int>("IdUser")
.HasColumnType("integer")
.HasColumnName("id_user")
.HasComment("ID пользователя который внес/изменил запись");
b.Property<int>("IdWell")
.HasColumnType("integer")
.HasColumnName("id_well")
.HasComment("ID скважины");
b.Property<double?>("Radius")
.HasColumnType("double precision")
.HasColumnName("radius")
.HasComment("Радиус цели");
b.Property<DateTimeOffset>("UpdateDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("update_date")
.HasComment("Дата загрузки траектории");
b.Property<double>("VerticalDepth")
.HasColumnType("double precision")
.HasColumnName("vertical_depth")
.HasComment("Глубина вертикальная");
b.Property<double>("WellboreDepth")
.HasColumnType("double precision")
.HasColumnName("wellbore_depth")
.HasComment("Глубина по стволу");
b.Property<double>("ZenithAngle")
.HasColumnType("double precision")
.HasColumnName("zenith_angle")
.HasComment("Угол зенитный");
b.HasKey("Id");
b.HasIndex("IdUser");
b.HasIndex("IdWell");
b.ToTable("t_planned_trajectory");
b.HasComment("Загрузка плановой траектории");
});
modelBuilder.Entity("AsbCloudDb.Model.ProcessMaps.ProcessMapWellDrilling", b =>
{
b.Property<int>("Id")
@ -4121,11 +4023,6 @@ namespace AsbCloudDb.Migrations
IdPermission = 528
},
new
{
IdUserRole = 1,
IdPermission = 529
},
new
{
IdUserRole = 1,
IdPermission = 530
@ -5240,6 +5137,141 @@ namespace AsbCloudDb.Migrations
b.HasComment("Наработка талевого каната");
});
modelBuilder.Entity("AsbCloudDb.Model.Trajectory.TrajectoryFact", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<double>("AzimuthGeo")
.HasColumnType("double precision")
.HasColumnName("azimuth_geo")
.HasComment("Азимут Географ.");
b.Property<double>("AzimuthMagnetic")
.HasColumnType("double precision")
.HasColumnName("azimuth_magnetic")
.HasComment("Азимут Магнитный");
b.Property<string>("Comment")
.HasColumnType("text")
.HasColumnName("comment")
.HasComment("Комментарии");
b.Property<int>("IdUser")
.HasColumnType("integer")
.HasColumnName("id_user")
.HasComment("ID пользователя который внес/изменил запись");
b.Property<int>("IdWell")
.HasColumnType("integer")
.HasColumnName("id_well")
.HasComment("ID скважины");
b.Property<DateTimeOffset>("UpdateDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("update_date")
.HasComment("Дата загрузки траектории");
b.Property<double>("VerticalDepth")
.HasColumnType("double precision")
.HasColumnName("vertical_depth")
.HasComment("Глубина вертикальная");
b.Property<double>("WellboreDepth")
.HasColumnType("double precision")
.HasColumnName("wellbore_depth")
.HasComment("Глубина по стволу");
b.Property<double>("ZenithAngle")
.HasColumnType("double precision")
.HasColumnName("zenith_angle")
.HasComment("Угол зенитный");
b.HasKey("Id");
b.HasIndex("IdUser");
b.HasIndex("IdWell");
b.ToTable("t_trajectory_fact");
b.HasComment("Загрузка фактической траектории");
});
modelBuilder.Entity("AsbCloudDb.Model.Trajectory.TrajectoryPlan", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<double>("AzimuthGeo")
.HasColumnType("double precision")
.HasColumnName("azimuth_geo")
.HasComment("Азимут Географ.");
b.Property<double>("AzimuthMagnetic")
.HasColumnType("double precision")
.HasColumnName("azimuth_magnetic")
.HasComment("Азимут Магнитный");
b.Property<string>("Comment")
.HasColumnType("text")
.HasColumnName("comment")
.HasComment("Комментарии");
b.Property<int>("IdUser")
.HasColumnType("integer")
.HasColumnName("id_user")
.HasComment("ID пользователя который внес/изменил запись");
b.Property<int>("IdWell")
.HasColumnType("integer")
.HasColumnName("id_well")
.HasComment("ID скважины");
b.Property<double?>("Radius")
.HasColumnType("double precision")
.HasColumnName("radius")
.HasComment("Радиус цели");
b.Property<DateTimeOffset>("UpdateDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("update_date")
.HasComment("Дата загрузки траектории");
b.Property<double>("VerticalDepth")
.HasColumnType("double precision")
.HasColumnName("vertical_depth")
.HasComment("Глубина вертикальная");
b.Property<double>("WellboreDepth")
.HasColumnType("double precision")
.HasColumnName("wellbore_depth")
.HasComment("Глубина по стволу");
b.Property<double>("ZenithAngle")
.HasColumnType("double precision")
.HasColumnName("zenith_angle")
.HasComment("Угол зенитный");
b.HasKey("Id");
b.HasIndex("IdUser");
b.HasIndex("IdWell");
b.ToTable("t_trajectory_plan");
b.HasComment("Загрузка плановой траектории");
});
modelBuilder.Entity("AsbCloudDb.Model.User", b =>
{
b.Property<int>("Id")
@ -8115,7 +8147,7 @@ namespace AsbCloudDb.Migrations
b.Navigation("Well");
});
modelBuilder.Entity("AsbCloudDb.Model.DailyReports.DailyReport", b =>
modelBuilder.Entity("AsbCloudDb.Model.DailyReport.DailyReport", b =>
{
b.HasOne("AsbCloudDb.Model.Well", "Well")
.WithMany()
@ -8387,25 +8419,6 @@ namespace AsbCloudDb.Migrations
b.Navigation("Well");
});
modelBuilder.Entity("AsbCloudDb.Model.PlannedTrajectory", b =>
{
b.HasOne("AsbCloudDb.Model.User", "User")
.WithMany()
.HasForeignKey("IdUser")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AsbCloudDb.Model.Well", "Well")
.WithMany()
.HasForeignKey("IdWell")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
b.Navigation("Well");
});
modelBuilder.Entity("AsbCloudDb.Model.ProcessMaps.ProcessMapWellDrilling", b =>
{
b.HasOne("AsbCloudDb.Model.User", "User")
@ -8713,6 +8726,44 @@ namespace AsbCloudDb.Migrations
b.Navigation("Telemetry");
});
modelBuilder.Entity("AsbCloudDb.Model.Trajectory.TrajectoryFact", b =>
{
b.HasOne("AsbCloudDb.Model.User", "User")
.WithMany()
.HasForeignKey("IdUser")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AsbCloudDb.Model.Well", "Well")
.WithMany()
.HasForeignKey("IdWell")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
b.Navigation("Well");
});
modelBuilder.Entity("AsbCloudDb.Model.Trajectory.TrajectoryPlan", b =>
{
b.HasOne("AsbCloudDb.Model.User", "User")
.WithMany()
.HasForeignKey("IdUser")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AsbCloudDb.Model.Well", "Well")
.WithMany()
.HasForeignKey("IdWell")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
b.Navigation("Well");
});
modelBuilder.Entity("AsbCloudDb.Model.User", b =>
{
b.HasOne("AsbCloudDb.Model.Company", "Company")

View File

@ -7,6 +7,7 @@ using AsbCloudDb.Model.DailyReports;
using AsbCloudDb.Model.Manuals;
using AsbCloudDb.Model.ProcessMaps;
using AsbCloudDb.Model.WellSections;
using AsbCloudDb.Model.Trajectory;
namespace AsbCloudDb.Model
{
@ -18,7 +19,7 @@ namespace AsbCloudDb.Model
public virtual DbSet<DailyReport> DailyReports => Set <DailyReport>();
public virtual DbSet<Deposit> Deposits => Set<Deposit>();
public virtual DbSet<DetectedOperation> DetectedOperations => Set<DetectedOperation>();
public virtual DbSet<PlannedTrajectory> PlannedTrajectories => Set<PlannedTrajectory>();
public virtual DbSet<TrajectoryPlan> TrajectoriesPlan => Set<TrajectoryPlan>();
public virtual DbSet<ProcessMapWellDrilling> ProcessMapWellDrillings => Set<ProcessMapWellDrilling>();
public virtual DbSet<ProcessMapWellReam> ProcessMapWellReams => Set<ProcessMapWellReam>();
public virtual DbSet<DrillingProgramPart> DrillingProgramParts => Set<DrillingProgramPart>();
@ -61,6 +62,7 @@ namespace AsbCloudDb.Model
public virtual DbSet<LimitingParameter> LimitingParameter => Set<LimitingParameter>();
public virtual DbSet<TelemetryWirelineRunOut> TelemetryWirelineRunOut => Set<TelemetryWirelineRunOut>();
public virtual DbSet<TrajectoryFact> TrajectoriesFact => Set<TrajectoryFact>();
// GTR WITS
public DbSet<WitsItemFloat> WitsItemFloat => Set<WitsItemFloat>();

View File

@ -11,6 +11,7 @@ using AsbCloudDb.Model.DailyReports;
using AsbCloudDb.Model.Manuals;
using AsbCloudDb.Model.ProcessMaps;
using AsbCloudDb.Model.WellSections;
using AsbCloudDb.Model.Trajectory;
namespace AsbCloudDb.Model
{
@ -22,7 +23,7 @@ namespace AsbCloudDb.Model
DbSet<DailyReport> DailyReports { get; }
DbSet<Deposit> Deposits { get; }
DbSet<DetectedOperation> DetectedOperations { get; }
DbSet<PlannedTrajectory> PlannedTrajectories { get; }
DbSet<TrajectoryPlan> TrajectoriesPlan { get; }
DbSet<ProcessMapWellDrilling> ProcessMapWellDrillings { get; }
DbSet<ProcessMapWellReam> ProcessMapWellReams { get; }
DbSet<DrillingProgramPart> DrillingProgramParts { get; }
@ -79,6 +80,7 @@ namespace AsbCloudDb.Model
DbSet<ManualDirectory> ManualDirectories { get; }
DbSet<Contact> Contacts { get; }
DbSet<DrillTest> DrillTests { get; }
DbSet<TrajectoryFact> TrajectoriesFact { get; }
DbSet<WellSectionPlan> PlanWellSections { get; }
DatabaseFacade Database { get; }

View File

@ -3,10 +3,9 @@ using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model
namespace AsbCloudDb.Model.Trajectory
{
[Table("t_planned_trajectory"), Comment("Загрузка плановой траектории")]
public class PlannedTrajectory : IId, IWellRelated
public abstract class Trajectory : IId, IWellRelated
{
[Column("id"), Key]
public int Id { get; set; }
@ -38,9 +37,6 @@ namespace AsbCloudDb.Model
[Column("comment"), Comment("Комментарии")]
public string? Comment { get; set; }
[Column("radius"), Comment("Радиус цели")]
public double? Radius { get; set; }
[ForeignKey(nameof(IdWell))]
public virtual Well Well { get; set; } = null!;

View File

@ -0,0 +1,10 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model.Trajectory
{
[Table("t_trajectory_fact"), Comment("Загрузка фактической траектории")]
public class TrajectoryFact : Trajectory
{
}
}

View File

@ -0,0 +1,12 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model.Trajectory
{
[Table("t_trajectory_plan"), Comment("Загрузка плановой траектории")]
public class TrajectoryPlan : Trajectory
{
[Column("radius"), Comment("Радиус цели")]
public double? Radius { get; set; }
}
}

View File

@ -113,17 +113,12 @@ sudo -u postgres psql
SELECT * FROM pg_stat_replication;
```
7. Для включения синхронного режима необходимо выполнить следующую команду
```
ALTER SYSTEM SET synchronous_standby_names TO '*';
```
7. Сделать рестарт primary-сервера.
8. Сделать рестарт primary-сервера.
9. Внести запись в любую таблицу базы данных primary-сервера
10. Убедиться, что соответствующая запись появилась в таблице базы данных standby-сервера
11. Попытаться внести запись в таблицу базы данных standby-сервера.
12. Убедиться, что операция завершилась с ошибкой
8. Внести запись в любую таблицу базы данных primary-сервера
9. Убедиться, что соответствующая запись появилась в таблице базы данных standby-сервера
10. Попытаться внести запись в таблицу базы данных standby-сервера.
11. Убедиться, что операция завершилась с ошибкой
> cannot execute OPERATION in a read-only transaction

View File

@ -14,7 +14,10 @@
<None Remove="CommonLibs\Readme.md" />
<None Remove="Services\DailyReport\DailyReportTemplate.xlsx" />
<None Remove="Services\DrillTestReport\DrillTestReportTemplate.xlsx" />
<None Remove="Services\Trajectory\FactTrajectoryTemplate.xlsx" />
<None Remove="Services\Trajectory\NnbTrajectoryTemplate.xlsx" />
<None Remove="Services\Trajectory\PlannedTrajectoryTemplate.xlsx" />
<None Remove="Services\Trajectory\Templates\TrajectoryFactNnbTemplate.xlsx" />
<None Remove="Services\WellOperationService\ScheduleReportTemplate.xlsx" />
<None Remove="Services\WellOperationService\WellOperationImportTemplate.xlsx" />
<None Remove="Services\DailyReport\DailyReportBlocks\" />
@ -33,7 +36,9 @@
<EmbeddedResource Include="Services\DetectOperations\DetectOperations.xlsx" />
<EmbeddedResource Include="Services\DailyReport\DailyReportTemplate.xlsx" />
<EmbeddedResource Include="Services\DrillTestReport\DrillTestReportTemplate.xlsx" />
<EmbeddedResource Include="Services\Trajectory\PlannedTrajectoryTemplate.xlsx" />
<EmbeddedResource Include="Services\Trajectory\Templates\TrajectoryFactNnbTemplate.xlsx" />
<EmbeddedResource Include="Services\Trajectory\Templates\TrajectoryFactManualTemplate.xlsx" />
<EmbeddedResource Include="Services\Trajectory\Templates\TrajectoryPlanTemplate.xlsx" />
<EmbeddedResource Include="Services\WellOperationService\ScheduleReportTemplate.xlsx" />
<EmbeddedResource Include="Services\WellOperationImport\Files\WellOperationImportTemplate.xlsx" />
<EmbeddedResource Include="Services\WellOperationImport\Files\Dictionaries\Operations.txt" />

View File

@ -5,6 +5,7 @@ using AsbCloudApp.Data.Manuals;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Data.Subsystems;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Data.WellOperationImport.Options;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
@ -17,6 +18,7 @@ using AsbCloudDb.Model;
using AsbCloudDb.Model.Manuals;
using AsbCloudDb.Model.ProcessMaps;
using AsbCloudDb.Model.Subsystems;
using AsbCloudDb.Model.Trajectory;
using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Repository;
using AsbCloudInfrastructure.Services;
@ -29,6 +31,8 @@ using AsbCloudInfrastructure.Services.ProcessMaps.WellDrilling;
using AsbCloudInfrastructure.Services.SAUB;
using AsbCloudInfrastructure.Services.Subsystems;
using AsbCloudInfrastructure.Services.Trajectory;
using AsbCloudInfrastructure.Services.Trajectory.Export;
using AsbCloudInfrastructure.Services.Trajectory.Import;
using AsbCloudInfrastructure.Services.WellOperationImport;
using AsbCloudInfrastructure.Services.WellOperationImport.FileParser;
using AsbCloudInfrastructure.Services.WellOperationService;
@ -202,7 +206,11 @@ namespace AsbCloudInfrastructure
services.AddTransient<IWellService, WellService>();
services.AddTransient<IWellOperationImportService, WellOperationImportService>();
services.AddTransient<IProcessMapReportWellDrillingExportService, ProcessMapReportWellDrillingExportService>();
services.AddTransient<IPlannedTrajectoryImportService, PlannedTrajectoryImportService>();
services.AddTransient<TrajectoryPlanExportService>();
services.AddTransient<TrajectoryFactManualExportService>();
services.AddTransient<TrajectoryFactNnbExportService>();
services.AddTransient<TrajectoryPlanParserService>();
services.AddTransient<TrajectoryFactManualParserService>();
services.AddTransient<IWellOperationRepository, WellOperationRepository>();
services.AddTransient<IDailyReportService, DailyReportService>();
services.AddTransient<IDetectedOperationService, DetectedOperationService>();
@ -265,16 +273,17 @@ namespace AsbCloudInfrastructure
services.AddTransient<ILimitingParameterRepository, LimitingParameterRepository>();
services.AddTransient<ITelemetryWirelineRunOutRepository, TelemetryWirelineRunOutRepository>();
services.AddTransient<IWellFinalDocumentsRepository, WellFinalDocumentsRepository>();
services.AddTransient<ITrajectoryPlanRepository, TrajectoryPlanRepository>();
services.AddTransient<ITrajectoryFactRepository, TrajectoryFactRepository>();
services.AddTransient<ITrajectoryEditableRepository<TrajectoryGeoPlanDto>, TrajectoryEditableRepository<TrajectoryPlan, TrajectoryGeoPlanDto>>();
services.AddTransient<ITrajectoryEditableRepository<TrajectoryGeoFactDto>, TrajectoryEditableRepository<TrajectoryFact, TrajectoryGeoFactDto>>();
services.AddTransient<ITrajectoryNnbRepository, TrajectoryNnbRepository>();
services.AddTransient<IFaqRepository, FaqRepository>();
services.AddTransient<ISlipsStatService, SlipsStatService>();
services.AddTransient<IWellContactService, WellContactService>();
services.AddTransient<ICrudRepository<WellSectionTypeDto>, CrudCacheRepositoryBase<WellSectionTypeDto,
WellSectionType>>();
services.AddTransient<ICrudRepository<SubsystemDto>, CrudCacheRepositoryBase<SubsystemDto, Subsystem>>();
services.AddTransient<ICrudRepository<PermissionDto>, CrudCacheRepositoryBase<PermissionDto, Permission>>();
// TelemetryData services

View File

@ -1,8 +1,9 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudDb.Model.Trajectory;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
@ -13,25 +14,31 @@ using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository
{
public class TrajectoryPlanRepository : ITrajectoryPlanRepository
/// <summary>
/// CRUD-репозиторий для работы с траекториями (плановыми и фактическими)
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="Tdto"></typeparam>
public class TrajectoryEditableRepository<TEntity, Tdto> : ITrajectoryEditableRepository<Tdto>
where TEntity : Trajectory
where Tdto : TrajectoryGeoDto
{
private readonly IAsbCloudDbContext db;
private readonly IWellService wellService;
public TrajectoryPlanRepository(IAsbCloudDbContext db, IWellService wellService)
public TrajectoryEditableRepository(IAsbCloudDbContext db, IWellService wellService)
{
this.db = db;
this.wellService = wellService;
}
/// <inheritdoc/>
public async Task<int> AddRangeAsync(IEnumerable<TrajectoryGeoPlanDto> plannedTrajectoryRows, CancellationToken token)
public async Task<int> AddRangeAsync(IEnumerable<Tdto> trajectoryRows, CancellationToken token)
{
var idWell = plannedTrajectoryRows.First().IdWell;
if (!plannedTrajectoryRows.All(r => r.IdWell == idWell))
throw new ArgumentInvalidException(nameof(plannedTrajectoryRows), "Все строки должны относиться к одной скважине");
var idWell = trajectoryRows.First().IdWell;
if (!trajectoryRows.All(r => r.IdWell == idWell))
throw new ArgumentInvalidException(nameof(trajectoryRows), "Все строки должны относиться к одной скважине");
var offsetHours = wellService.GetTimezone(idWell).Hours;
var entities = plannedTrajectoryRows
var entities = trajectoryRows
.Select(e =>
{
var entity = Convert(e, offsetHours);
@ -39,80 +46,75 @@ namespace AsbCloudInfrastructure.Repository
return entity;
});
db.PlannedTrajectories.AddRange(entities);
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
db.Set<TEntity>().AddRange(entities);
return await db.SaveChangesAsync(token);
}
/// <inheritdoc/>
public async Task<int> AddAsync(TrajectoryGeoPlanDto plannedTrajectoryRow, CancellationToken token)
public async Task<int> AddAsync(Tdto trajectoryRow, CancellationToken token)
{
var offsetHours = wellService.GetTimezone(plannedTrajectoryRow.IdWell).Hours;
var entity = Convert(plannedTrajectoryRow, offsetHours);
var offsetHours = wellService.GetTimezone(trajectoryRow.IdWell).Hours;
var entity = Convert(trajectoryRow, offsetHours);
entity.Id = 0;
db.PlannedTrajectories.Add(entity);
db.Set<TEntity>().Add(entity);
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<int> DeleteRangeAsync(IEnumerable<int> ids, CancellationToken token)
{
var query = db.PlannedTrajectories
var query = db.Set<TEntity>()
.Where(e => ids.Contains(e.Id));
db.PlannedTrajectories.RemoveRange(query);
db.Set<TEntity>().RemoveRange(query);
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<int> DeleteByIdWellAsync(int idWell, CancellationToken token)
{
var query = db.PlannedTrajectories
var query = db.Set<TEntity>()
.Where(e => e.IdWell == idWell);
db.PlannedTrajectories.RemoveRange(query);
db.Set<TEntity>().RemoveRange(query);
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<IEnumerable<TrajectoryGeoPlanDto>> GetAsync(int idWell, CancellationToken token)
public async Task<IEnumerable<Tdto>> GetAsync(int idWell, CancellationToken token)
{
var well = wellService.GetOrDefault(idWell)
?? throw new ArgumentInvalidException(nameof(idWell), "idWell doesn`t exist");
var offsetHours = well.Timezone.Hours;
var query = db.PlannedTrajectories
var query = db.Set<TEntity>()
.AsNoTracking()
.Where(x => x.IdWell == idWell);
.Where(x => x.IdWell == well.Id);
var entities = await query
.OrderBy(e => e.WellboreDepth)
.ToArrayAsync(token);
var result = entities
.Select(r => Convert(r, offsetHours));
return result;
}
/// <inheritdoc/>
public async Task<int> UpdateAsync(TrajectoryGeoPlanDto row, CancellationToken token)
public async Task<int> UpdateAsync(Tdto row, CancellationToken token)
{
var offsetHours = wellService.GetTimezone(row.IdWell).Hours;
var entity = Convert(row, offsetHours);
db.PlannedTrajectories.Update(entity);
db.Set<TEntity>().Update(entity);
return await db.SaveChangesAsync(token)
.ConfigureAwait(false);
}
private TrajectoryGeoPlanDto Convert(PlannedTrajectory entity, double offsetHours)
private static Tdto Convert(TEntity entity, double offsetHours)
{
var dto = entity.Adapt<TrajectoryGeoPlanDto>();
var dto = entity.Adapt<Tdto>();
dto.UpdateDate = entity.UpdateDate.ToRemoteDateTime(offsetHours);
return dto;
}
private PlannedTrajectory Convert(TrajectoryGeoPlanDto dto, double offsetHours)
private static TEntity Convert(Tdto dto, double offsetHours)
{
var entity = dto.Adapt<PlannedTrajectory>();
var entity = dto.Adapt<TEntity>();
entity.UpdateDate = DateTime.Now.ToUtcDateTimeOffset(offsetHours);
return entity;
}

View File

@ -1,65 +0,0 @@
using AsbCloudApp.Data;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudDb.Model;
using AsbCloudDb.Model.WITS;
using Microsoft.EntityFrameworkCore;
namespace AsbCloudInfrastructure.Repository;
public class TrajectoryFactRepository : ITrajectoryFactRepository
{
private readonly IAsbCloudDbContext dbContext;
public TrajectoryFactRepository(IAsbCloudDbContext dbContext)
{
this.dbContext = dbContext;
}
public async Task<IEnumerable<TrajectoryGeoFactDto>> GetAsync(TrajectoryGeoFactRequest request, CancellationToken token) =>
(await BuildQuery(request)
.Where(coord => coord.Deptsvym.HasValue &&
coord.Svyinc.HasValue &&
coord.Svyazc.HasValue)
.AsNoTracking()
.ToArrayAsync(token))
.Select(r => new TrajectoryGeoFactDto
{
IdWell = request.IdWell,
AzimuthMagnetic = r.Svymtf,
VerticalDepth = r.Deptsvyv,
WellboreDepth = r.Deptsvym!.Value,
ZenithAngle = r.Svyinc!.Value,
AzimuthGeo = r.Svyazc!.Value
});
public Task<IEnumerable<TrajectoryGeoFactDto>> GetAsync(int idWell, CancellationToken token) =>
GetAsync(new TrajectoryGeoFactRequest
{
IdWell = idWell
}, token);
private IQueryable<Record7> BuildQuery(TrajectoryGeoFactRequest request)
{
var well = dbContext.Wells.SingleOrDefault(w => w.Id == request.IdWell);
if (well is null)
throw new ArgumentInvalidException($"Скважина с Id: {request.IdWell} не найдена", nameof(request.IdWell));
var query = dbContext.Record7.Where(r => r.IdTelemetry == well.IdTelemetry)
.Where(x => x.IdTelemetry == well.IdTelemetry);
if (request.GeDate.HasValue)
query = query.Where(r => r.DateTime >= request.GeDate.Value);
if (request.LeDate.HasValue)
query = query.Where(r => r.DateTime <= request.LeDate.Value);
return query.OrderBy(e => e.Deptsvym);
}
}

View File

@ -0,0 +1,76 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudDb.Model;
using AsbCloudDb.Model.WITS;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository
{
public class TrajectoryNnbRepository : ITrajectoryNnbRepository
{
private readonly IAsbCloudDbContext db;
public TrajectoryNnbRepository(IAsbCloudDbContext db)
{
this.db = db;
}
private IQueryable<Record7> BuildQuery(TrajectoryRequest request)
{
var well = db.Wells.SingleOrDefault(w => w.Id == request.IdWell);
if (well is null)
throw new ArgumentInvalidException($"Скважина с Id: {request.IdWell} не найдена", nameof(request.IdWell));
var query = db.Record7.Where(r => r.IdTelemetry == well.IdTelemetry)
.Where(x => x.IdTelemetry == well.IdTelemetry);
if (request.GeDate.HasValue)
query = query.Where(r => r.DateTime >= request.GeDate.Value);
if (request.LeDate.HasValue)
query = query.Where(r => r.DateTime <= request.LeDate.Value);
return query.OrderBy(e => e.Deptsvym);
}
public async Task<IEnumerable<TrajectoryGeoFactDto>> GetAsync(int idWell, CancellationToken token)
{
var request = new TrajectoryRequest()
{
IdWell = idWell,
};
var result = await GetByRequestAsync(request, token);
return result;
}
public async Task<IEnumerable<TrajectoryGeoFactDto>> GetByRequestAsync(TrajectoryRequest request, CancellationToken token)
{
var entities = (await BuildQuery(request)
.Where(coord => coord.Deptsvym.HasValue &&
coord.Svyinc.HasValue &&
coord.Svyazc.HasValue)
.AsNoTracking()
.ToArrayAsync(token));
var result = entities
.Select(coord => new TrajectoryGeoFactDto
{
IdWell = request.IdWell,
AzimuthMagnetic = coord.Svymtf,
VerticalDepth = coord.Deptsvyv,
WellboreDepth = coord.Deptsvym!.Value,
ZenithAngle = coord.Svyinc!.Value,
AzimuthGeo = coord.Svyazc!.Value
})
.ToArray();
return result;
}
}
}

View File

@ -19,13 +19,14 @@ using AsbCloudApp.Services.ProcessMaps.WellDrilling;
using AsbCloudApp.Services.Subsystems;
using AsbCloudDb.Model;
using Mapster;
using AsbCloudApp.Data.Trajectory;
namespace AsbCloudInfrastructure.Services.DailyReport;
public class DailyReportService : IDailyReportService
{
private readonly IWellService wellService;
private readonly ITrajectoryFactRepository trajectoryFactRepository;
private readonly ITrajectoryNnbRepository trajectoryFactNnbRepository;
private readonly IDailyReportRepository dailyReportRepository;
private readonly IScheduleRepository scheduleRepository;
private readonly IWellOperationRepository wellOperationRepository;
@ -34,7 +35,7 @@ public class DailyReportService : IDailyReportService
private readonly IDetectedOperationService detectedOperationService;
public DailyReportService(IWellService wellService,
ITrajectoryFactRepository trajectoryFactRepository,
ITrajectoryNnbRepository trajectoryFactNnbRepository,
IDailyReportRepository dailyReportRepository,
IScheduleRepository scheduleRepository,
IWellOperationRepository wellOperationRepository,
@ -43,7 +44,7 @@ public class DailyReportService : IDailyReportService
IDetectedOperationService detectedOperationService)
{
this.wellService = wellService;
this.trajectoryFactRepository = trajectoryFactRepository;
this.trajectoryFactNnbRepository = trajectoryFactNnbRepository;
this.dailyReportRepository = dailyReportRepository;
this.scheduleRepository = scheduleRepository;
this.wellOperationRepository = wellOperationRepository;
@ -259,7 +260,7 @@ public class DailyReportService : IDailyReportService
private async Task AddTrajectoryBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
{
var trajectory = (await trajectoryFactRepository.GetAsync(new TrajectoryGeoFactRequest
var trajectory = (await trajectoryFactNnbRepository.GetByRequestAsync(new TrajectoryRequest
{
IdWell = dailyReport.IdWell,
GeDate = dailyReport.Date,

View File

@ -0,0 +1,85 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using ClosedXML.Excel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.Trajectory.Export
{
public abstract class TrajectoryExportService<T> where T : TrajectoryGeoDto
{
private readonly IWellService wellService;
private readonly ITrajectoryRepository<T> trajectoryRepository;
public abstract string templateFileName { get; }
public abstract string usingTemplateFile { get; }
public abstract string sheetName { get; }
public abstract int headerRowsCount { get; }
public TrajectoryExportService(IWellService wellService, ITrajectoryRepository<T> trajectoryRepository)
{
this.wellService = wellService;
this.trajectoryRepository = trajectoryRepository;
}
protected abstract void AddCoordinatesToRow(IXLRow row, T trajectory);
public async Task<Stream> ExportAsync(int idWell, CancellationToken token)
{
var trajectorys = await trajectoryRepository.GetAsync(idWell, token);
return MakeExelFileStream(trajectorys);
}
public async Task<string> GetFileNameAsync(int idWell, CancellationToken token)
{
var caption = await wellService.GetWellCaptionByIdAsync(idWell, token);
return string.Format("{0}_{1}", caption, templateFileName);
}
public Stream GetTemplateFile()
{
var stream = System.Reflection.Assembly.GetExecutingAssembly()
.GetManifestResourceStream($"{usingTemplateFile}.{templateFileName}");
if (stream is null)
throw new Exception($"Область {usingTemplateFile} не содержит файла с названием {templateFileName}");
return stream;
}
private Stream MakeExelFileStream(IEnumerable<T> trajectories)
{
using Stream ecxelTemplateStream = GetTemplateFile();
using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled);
AddTrajecoryToWorkbook(workbook, trajectories);
MemoryStream memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { });
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
private void AddTrajecoryToWorkbook(XLWorkbook workbook, IEnumerable<T> trajectories)
{
if (trajectories.Any())
{
var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName);
if (sheet is null)
throw new FileFormatException($"Лист с именем {sheetName} отсутствует, либо имеет некорректное название");
AddTrajecoryToSheet(sheet, trajectories);
}
}
private void AddTrajecoryToSheet(IXLWorksheet sheet, IEnumerable<T> trajectories)
{
var rowList = trajectories.ToList();
for (int i = 0; i < rowList.Count; i++)
{
var row = sheet.Row(1 + i + headerRowsCount);
AddCoordinatesToRow(row, rowList[i]);
}
}
}
}

View File

@ -0,0 +1,35 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.Trajectory.Export
{
public class TrajectoryFactManualExportService : TrajectoryExportService<TrajectoryGeoFactDto>
{
public override string templateFileName { get; } = "TrajectoryFactManualTemplate.xlsx";
public override string usingTemplateFile { get; } = "AsbCloudInfrastructure.Services.Trajectory.Templates";
public override string sheetName { get; } = "Фактическая траектория";
public override int headerRowsCount { get; } = 2;
public TrajectoryFactManualExportService(
IWellService wellService,
ITrajectoryEditableRepository<TrajectoryGeoFactDto> factTrajectoryService)
: base(wellService, factTrajectoryService)
{
}
protected override void AddCoordinatesToRow(IXLRow row, TrajectoryGeoFactDto trajectory)
{
row.Cell(1).Value = trajectory.WellboreDepth;
row.Cell(2).Value = trajectory.ZenithAngle;
row.Cell(3).Value = trajectory.AzimuthGeo;
row.Cell(4).Value = trajectory.AzimuthMagnetic;
row.Cell(5).Value = trajectory.VerticalDepth;
row.Cell(6).Value = trajectory.Comment;
}
}
}

View File

@ -0,0 +1,35 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.Trajectory.Export
{
public class TrajectoryFactNnbExportService : TrajectoryExportService<TrajectoryGeoFactDto>
{
public override string templateFileName { get; } = "TrajectoryFactNnbTemplate.xlsx";
public override string usingTemplateFile { get; } = "AsbCloudInfrastructure.Services.Trajectory.Templates";
public override string sheetName { get; } = "Фактическая ннб-траектория";
public override int headerRowsCount { get; } = 2;
public TrajectoryFactNnbExportService(
IWellService wellService,
ITrajectoryNnbRepository nnbTrajectoryService)
: base(wellService, nnbTrajectoryService)
{
}
protected override void AddCoordinatesToRow(IXLRow row, TrajectoryGeoFactDto trajectory)
{
row.Cell(1).Value = trajectory.WellboreDepth;
row.Cell(2).Value = trajectory.ZenithAngle;
row.Cell(3).Value = trajectory.AzimuthGeo;
row.Cell(4).Value = trajectory.AzimuthMagnetic;
row.Cell(5).Value = trajectory.VerticalDepth;
row.Cell(6).Value = trajectory.Comment;
}
}
}

View File

@ -0,0 +1,38 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.Trajectory.Export
{
public class TrajectoryPlanExportService : TrajectoryExportService<TrajectoryGeoPlanDto>
{
/*
* password for PlannedTrajectoryTemplate.xlsx is Drill2022
*/
public override string templateFileName { get; } = "TrajectoryPlanTemplate.xlsx";
public override string usingTemplateFile { get; } = "AsbCloudInfrastructure.Services.Trajectory.Templates";
public override string sheetName { get; } = "Плановая траектория";
public override int headerRowsCount { get; } = 2;
public TrajectoryPlanExportService(
IWellService wellService,
ITrajectoryEditableRepository<TrajectoryGeoPlanDto> trajectoryPlanService)
: base(wellService, trajectoryPlanService)
{
}
protected override void AddCoordinatesToRow(IXLRow row, TrajectoryGeoPlanDto trajectory)
{
row.Cell(1).Value = trajectory.WellboreDepth;
row.Cell(2).Value = trajectory.ZenithAngle;
row.Cell(3).Value = trajectory.AzimuthGeo;
row.Cell(4).Value = trajectory.AzimuthMagnetic;
row.Cell(5).Value = trajectory.VerticalDepth;
row.Cell(6).Value = trajectory.Radius;
row.Cell(7).Value = trajectory.Comment;
}
}
}

View File

@ -0,0 +1,30 @@
using AsbCloudApp.Data.Trajectory;
using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.Trajectory.Import
{
public class TrajectoryFactManualParserService : TrajectoryParserService<TrajectoryGeoFactDto>
{
public override string templateFileName { get; } = "TrajectoryFactManualTemplate.xlsx";
public override string usingTemplateFile { get; } = "AsbCloudInfrastructure.Services.Trajectory.Templates";
public override string sheetName { get; } = "Фактическая траектория";
public override int headerRowsCount { get; } = 2;
protected override TrajectoryGeoFactDto ParseRow(IXLRow row)
{
var trajectoryRow = new TrajectoryGeoFactDto
{
WellboreDepth = row.Cell(1).GetCellValue<double>(),
ZenithAngle = row.Cell(2).GetCellValue<double>(),
AzimuthGeo = row.Cell(3).GetCellValue<double>(),
AzimuthMagnetic = row.Cell(4).GetCellValue<double>(),
VerticalDepth = row.Cell(5).GetCellValue<double>(),
Comment = row.Cell(6).GetCellValue<string?>()
};
//TODO: Добавить валидацию модели IValidatableObject
return trajectoryRow;
}
}
}

View File

@ -0,0 +1,78 @@
using AsbCloudApp.Data.Trajectory;
using ClosedXML.Excel;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace AsbCloudInfrastructure.Services.Trajectory.Import
{
public abstract class TrajectoryParserService<T>
where T : TrajectoryGeoDto
{
public abstract string templateFileName { get; }
public abstract string usingTemplateFile { get; }
public abstract string sheetName { get; }
public abstract int headerRowsCount { get; }
protected abstract T ParseRow(IXLRow row);
public IEnumerable<T> Import(Stream stream)
{
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
var trajectoryRows = ParseFileStream(stream);
return trajectoryRows;
}
private IEnumerable<T> ParseFileStream(Stream stream)
{
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
return ParseWorkbook(workbook);
}
private IEnumerable<T> ParseWorkbook(IXLWorkbook workbook)
{
var sheetTrajectory = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName);
if (sheetTrajectory is null)
throw new FileFormatException($"Книга excel не содержит листа {sheetName}.");
var trajectoryRows = ParseSheet(sheetTrajectory);
return trajectoryRows;
}
private IEnumerable<T> ParseSheet(IXLWorksheet sheet)
{
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 6)
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов.");
var count = sheet.RowsUsed().Count() - headerRowsCount;
if (count > 1024)
throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество строк.");
if (count <= 0)
throw new FileFormatException($"Лист {sheet.Name} некорректного формата либо пустой");
var trajectoryRows = new List<T>(count);
var parseErrors = new List<string>();
for (int i = 0; i < count; i++)
{
var row = sheet.Row(1 + i + headerRowsCount);
try
{
var trajectoryRow = ParseRow(row);
trajectoryRows.Add(trajectoryRow);
}
catch (FileFormatException ex)
{
parseErrors.Add(ex.Message);
}
}
if (parseErrors.Any())
throw new FileFormatException(string.Join("\r\n", parseErrors));
return trajectoryRows;
}
}
}

View File

@ -0,0 +1,33 @@
using AsbCloudApp.Data.Trajectory;
using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.Trajectory.Import
{
public class TrajectoryPlanParserService : TrajectoryParserService<TrajectoryGeoPlanDto>
{
public override string templateFileName { get; } = "TrajectoryPlanTemplate.xlsx";
public override string usingTemplateFile { get; } = "AsbCloudInfrastructure.Services.Trajectory.Templates";
public override string sheetName { get; } = "Плановая траектория";
public override int headerRowsCount { get; } = 2;
protected override TrajectoryGeoPlanDto ParseRow(IXLRow row)
{
var trajectoryRow = new TrajectoryGeoPlanDto
{
WellboreDepth = row.Cell(1).GetCellValue<double>(),
ZenithAngle = row.Cell(2).GetCellValue<double>(),
AzimuthGeo = row.Cell(3).GetCellValue<double>(),
AzimuthMagnetic = row.Cell(4).GetCellValue<double>(),
VerticalDepth = row.Cell(5).GetCellValue<double>(),
Radius = row.Cell(6).GetCellValue<double>(),
Comment = row.Cell(7).GetCellValue<string?>()
};
//TODO: Добавить валидацию модели IValidatableObject
return trajectoryRow;
}
}
}

View File

@ -1,196 +0,0 @@
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using ClosedXML.Excel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.Trajectory
{
public class PlannedTrajectoryImportService : IPlannedTrajectoryImportService
{
/*
* password for PlannedTrajectoryTemplate.xlsx is Drill2022
*/
private readonly IWellService wellService;
private readonly ITrajectoryPlanRepository plannedTrajectoryService;
private const string templateFileName = "PlannedTrajectoryTemplate.xlsx";
private const string usingTemplateFile = "AsbCloudInfrastructure.Services.Trajectory";
private const string sheetNamePlannedTrajectory = "Плановая траектория";
private const int headerRowsCount = 2;
private const int ColumnWellboreDepth = 1;
private const int ColumnZenithAngle = 2;
private const int ColumnAzimuthGeo = 3;
private const int ColumnAzimuthMagnetic = 4;
private const int ColumnVerticalDepth = 5;
private const int ColumnRadius = 6;
private const int ColumnComment = 7;
public PlannedTrajectoryImportService(IWellService wellService, ITrajectoryPlanRepository plannedTrajectoryService)
{
this.wellService = wellService;
this.plannedTrajectoryService = plannedTrajectoryService;
}
public Stream GetTemplateFile()
{
var stream = System.Reflection.Assembly.GetExecutingAssembly()
.GetManifestResourceStream($"{usingTemplateFile}.{templateFileName}");
if (stream is null)
throw new Exception($"Область {usingTemplateFile} не содержит файла с названием {templateFileName}");
return stream;
}
public async Task<string> GetFileNameAsync(int idWell, CancellationToken token)
{
var fileName = await wellService.GetWellCaptionByIdAsync(idWell, token) + "_plannedTrajectory.xlsx";
return fileName;
}
public async Task<Stream> ExportAsync(int idWell, CancellationToken token)
{
var plannedTrajectorys = await plannedTrajectoryService.GetAsync(idWell, token);
return MakeExelFileStream(plannedTrajectorys);
}
private Stream MakeExelFileStream(IEnumerable<TrajectoryGeoPlanDto> plannedTrajectories)
{
using Stream ecxelTemplateStream = GetTemplateFile();
using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled);
AddPlannedTrajecoryToWorkbook(workbook, plannedTrajectories);
MemoryStream memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { });
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
private static void AddPlannedTrajecoryToWorkbook(XLWorkbook workbook, IEnumerable<TrajectoryGeoPlanDto> plannedTrajectories)
{
if (plannedTrajectories.Any())
{
var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlannedTrajectory);
if (sheet is null)
throw new FileFormatException($"Лист с именем {sheetNamePlannedTrajectory} отсутствует, либо имеет некорректное название");
AddPlannedTrajecoryToSheet(sheet, plannedTrajectories);
}
}
private static void AddPlannedTrajecoryToSheet(IXLWorksheet sheet, IEnumerable<TrajectoryGeoPlanDto> plannedTrajectories)
{
var rowList = plannedTrajectories.ToList();
for (int i = 0; i < rowList.Count; i++)
{
var row = sheet.Row(1 + i + headerRowsCount);
AddCoordinatesToRow(row, rowList[i]);
}
}
private static void AddCoordinatesToRow(IXLRow row, TrajectoryGeoPlanDto trajectory)
{
row.Cell(ColumnWellboreDepth).Value = trajectory.WellboreDepth;
row.Cell(ColumnZenithAngle).Value = trajectory.ZenithAngle;
row.Cell(ColumnAzimuthGeo).Value = trajectory.AzimuthGeo;
row.Cell(ColumnAzimuthMagnetic).Value = trajectory.AzimuthMagnetic;
row.Cell(ColumnVerticalDepth).Value = trajectory.VerticalDepth;
row.Cell(ColumnRadius).Value = trajectory.Radius;
row.Cell(ColumnComment).Value = trajectory.Comment;
}
public async Task<int> ImportAsync(int idWell, int idUser, Stream stream, bool deletePrevRows, CancellationToken token)
{
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
var trajectoryRows = ParseFileStream(stream);
foreach (var row in trajectoryRows)
{
row.IdWell = idWell;
row.IdUser = idUser;
}
var rowsCount = await SavePlannedTrajectoryAsync(idWell, trajectoryRows, deletePrevRows, token);
return rowsCount;
}
private async Task<int> SavePlannedTrajectoryAsync(int idWell, IEnumerable<TrajectoryGeoPlanDto> newRows, bool deletePrevRow, CancellationToken token)
{
if (deletePrevRow)
await plannedTrajectoryService.DeleteByIdWellAsync(idWell, token);
var rowsCount = await plannedTrajectoryService.AddRangeAsync(newRows, token);
return rowsCount;
}
private IEnumerable<TrajectoryGeoPlanDto> ParseFileStream(Stream stream)
{
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
return ParseWorkbook(workbook);
}
private IEnumerable<TrajectoryGeoPlanDto> ParseWorkbook(IXLWorkbook workbook)
{
var sheetPlannedTrajectory = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlannedTrajectory);
if (sheetPlannedTrajectory is null)
throw new FileFormatException($"Книга excel не содержит листа {sheetNamePlannedTrajectory}.");
var plannedTrajectoryRows = ParseSheet(sheetPlannedTrajectory);
return plannedTrajectoryRows;
}
private IEnumerable<TrajectoryGeoPlanDto> ParseSheet(IXLWorksheet sheet)
{
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 7)
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов.");
var count = sheet.RowsUsed().Count() - headerRowsCount;
if (count > 1024)
throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество строк.");
if (count <= 0)
throw new FileFormatException($"Лист {sheet.Name} некорректного формата либо пустой");
var trajectoryRows = new List<TrajectoryGeoPlanDto>(count);
var parseErrors = new List<string>();
for (int i = 0; i < count; i++)
{
var row = sheet.Row(1 + i + headerRowsCount);
try
{
var trajectoryRow = ParseRow(row);
trajectoryRows.Add(trajectoryRow);
}
catch (FileFormatException ex)
{
parseErrors.Add(ex.Message);
}
}
if (parseErrors.Any())
throw new FileFormatException(string.Join("\r\n", parseErrors));
return trajectoryRows;
}
private TrajectoryGeoPlanDto ParseRow(IXLRow row)
{
var trajectoryRow = new TrajectoryGeoPlanDto
{
WellboreDepth = row.Cell(ColumnWellboreDepth).GetCellValue<double>(),
ZenithAngle = row.Cell(ColumnZenithAngle).GetCellValue<double>(),
AzimuthGeo = row.Cell(ColumnAzimuthGeo).GetCellValue<double>(),
AzimuthMagnetic = row.Cell(ColumnAzimuthMagnetic).GetCellValue<double>(),
VerticalDepth = row.Cell(ColumnVerticalDepth).GetCellValue<double>(),
Radius = row.Cell(ColumnRadius).GetCellValue<double>(),
Comment = row.Cell(ColumnComment).GetCellValue<string?>()
};
return trajectoryRow;
}
}
}

View File

@ -0,0 +1 @@
password for PlannedTrajectoryTemplate.xlsx is Drill2022

View File

@ -1,5 +1,6 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using System;
using System.Collections.Generic;
using System.Linq;
@ -91,7 +92,7 @@ abstract class TrajectoryBaseService<TGeo, TCartesian>
class TrajectoryPlanService: TrajectoryBaseService<TrajectoryGeoPlanDto, TrajectoryCartesianPlanDto>
{
public TrajectoryPlanService(ITrajectoryPlanRepository repository)
public TrajectoryPlanService(ITrajectoryEditableRepository<TrajectoryGeoPlanDto> repository)
:base(repository)
{}
@ -109,7 +110,14 @@ class TrajectoryPlanService: TrajectoryBaseService<TrajectoryGeoPlanDto, Traject
class TrajectoryFactService : TrajectoryBaseService<TrajectoryGeoFactDto, TrajectoryCartesianFactDto>
{
public TrajectoryFactService(ITrajectoryFactRepository repository)
public TrajectoryFactService(ITrajectoryEditableRepository<TrajectoryGeoFactDto> repository)
: base(repository)
{ }
}
class TrajectoryNnbService : TrajectoryBaseService<TrajectoryGeoFactDto, TrajectoryCartesianFactDto>
{
public TrajectoryNnbService(ITrajectoryNnbRepository repository)
: base(repository)
{ }
}
@ -119,11 +127,16 @@ public class TrajectoryService
{
private TrajectoryPlanService trajectoryPlanService;
private TrajectoryFactService trajectoryFactService;
private TrajectoryNnbService trajectoryNnbService;
public TrajectoryService(ITrajectoryPlanRepository plannedRepository, ITrajectoryFactRepository factRepository)
public TrajectoryService(
ITrajectoryEditableRepository<TrajectoryGeoPlanDto> planRepository,
ITrajectoryEditableRepository<TrajectoryGeoFactDto> factRepository,
ITrajectoryNnbRepository nnbRepository)
{
trajectoryPlanService = new TrajectoryPlanService(plannedRepository);
trajectoryPlanService = new TrajectoryPlanService(planRepository);
trajectoryFactService = new TrajectoryFactService(factRepository);
trajectoryNnbService = new TrajectoryNnbService(nnbRepository);
}
/// <summary>
@ -132,12 +145,13 @@ public class TrajectoryService
/// <param name="idWell">ключ скважины</param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<PlanFactBase<IEnumerable<TrajectoryCartesianPlanDto>, IEnumerable<TrajectoryCartesianFactDto>>> GetTrajectoryCartesianAsync(int idWell, CancellationToken token)
public async Task<TrajectoryPlanFactDto<IEnumerable<TrajectoryCartesianPlanDto>, IEnumerable<TrajectoryCartesianFactDto>>> GetTrajectoryCartesianAsync(int idWell, CancellationToken token)
{
var result = new PlanFactBase<IEnumerable<TrajectoryCartesianPlanDto>, IEnumerable<TrajectoryCartesianFactDto>>();
var result = new TrajectoryPlanFactDto<IEnumerable<TrajectoryCartesianPlanDto>, IEnumerable<TrajectoryCartesianFactDto>>();
result.Plan = await trajectoryPlanService.GetAsync(idWell, token);
result.Fact = await trajectoryFactService.GetAsync(idWell, token);
result.FactManual = await trajectoryFactService.GetAsync(idWell, token);
result.FactNnb = await trajectoryNnbService.GetAsync(idWell, token);
return result;
}

View File

@ -2,12 +2,9 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperationImport;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services.WellOperationImport;
namespace AsbCloudInfrastructure.Services.WellOperationImport;
@ -25,14 +22,14 @@ public class WellOperationImportService : IWellOperationImportService
this.wellOperationRepository = wellOperationRepository;
}
public async Task ImportAsync(int idWell, int idUser, int idType, SheetDto sheet, bool deleteBeforeImport, CancellationToken cancellationToken)
public IEnumerable<WellOperationDto> Import(int idWell, int idUser, int idType, SheetDto sheet)
{
var validationErrors = new List<string>();
var sections = wellOperationRepository.GetSectionTypes();
var categories = wellOperationRepository.GetCategories(false);
var operations = new List<WellOperationDto>();
var wellOperations = new List<WellOperationDto>();
foreach (var row in sheet.Rows)
{
@ -62,14 +59,14 @@ public class WellOperationImportService : IWellOperationImportService
throw new FileFormatException(
$"Лист '{sheet.Name}'. Строка '{row.Number}' неправильно получена дата начала операции");
if (operations.LastOrDefault()?.DateStart > row.Date)
if (wellOperations.LastOrDefault()?.DateStart > row.Date)
throw new FileFormatException(
$"Лист '{sheet.Name}' строка '{row.Number}' дата позднее даты предыдущей операции");
if (row.Duration is not (>= 0d and <= 240d))
throw new FileFormatException($"Лист '{sheet.Name}'. Строка '{row.Number}' некорректная длительность операции");
operations.Add(new WellOperationDto
wellOperations.Add(new WellOperationDto
{
IdWell = idWell,
IdUser = idUser,
@ -89,26 +86,12 @@ public class WellOperationImportService : IWellOperationImportService
}
}
if (operations.Any() && operations.Min(o => o.DateStart) - operations.Max(o => o.DateStart) > drillingDurationLimitMax)
if (wellOperations.Any() && wellOperations.Min(o => o.DateStart) - wellOperations.Max(o => o.DateStart) > drillingDurationLimitMax)
validationErrors.Add($"Лист {sheet.Name} содержит диапазон дат больше {drillingDurationLimitMax}");
if (validationErrors.Any())
throw new FileFormatException(string.Join("\r\n", validationErrors));
if (!operations.Any())
return;
if (deleteBeforeImport)
{
var existingOperations = await wellOperationRepository.GetAsync(new WellOperationRequest
{
IdWell = idWell,
OperationType = idType
}, cancellationToken);
await wellOperationRepository.DeleteAsync(existingOperations.Select(o => o.Id), cancellationToken);
}
await wellOperationRepository.InsertRangeAsync(operations, cancellationToken);
return wellOperations;
}
}

View File

@ -8,6 +8,16 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Remove="UnitTests\Services\Trajectory\PlannedTrajectoryTemplate.xlsx" />
<None Remove="UnitTests\Services\Trajectory\Templates\TrajectoryFactManualTemplate.xlsx" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="UnitTests\Services\Trajectory\Templates\TrajectoryFactManualTemplate.xlsx" />
<EmbeddedResource Include="UnitTests\Services\Trajectory\Templates\TrajectoryPlanTemplate.xlsx" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="NSubstitute" Version="5.1.0" />

View File

@ -11,6 +11,7 @@ using AsbCloudApp.Data.DailyReport.Blocks.TimeBalance;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Data.ProcessMaps.Report;
using AsbCloudApp.Data.Subsystems;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
@ -199,7 +200,7 @@ public class DailyReportServiceTest
};
private readonly IWellService wellServiceMock = Substitute.For<IWellService>();
private readonly ITrajectoryFactRepository trajectoryFactRepositoryMock = Substitute.For<ITrajectoryFactRepository>();
private readonly ITrajectoryNnbRepository trajectoryFactNnbRepositoryMock = Substitute.For<ITrajectoryNnbRepository>();
private readonly IDailyReportRepository dailyReportRepositoryMock = Substitute.For<IDailyReportRepository>();
private readonly IScheduleRepository scheduleRepositoryMock = Substitute.For<IScheduleRepository>();
private readonly IWellOperationRepository wellOperationRepositoryMock = Substitute.For<IWellOperationRepository>();
@ -233,7 +234,7 @@ public class DailyReportServiceTest
};
dailyReportService = new DailyReportService(wellServiceMock,
trajectoryFactRepositoryMock,
trajectoryFactNnbRepositoryMock,
dailyReportRepositoryMock,
scheduleRepositoryMock,
wellOperationRepositoryMock,
@ -253,7 +254,7 @@ public class DailyReportServiceTest
wellServiceMock.GetOrDefaultAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(fakeWell);
trajectoryFactRepositoryMock.GetAsync(Arg.Any<TrajectoryGeoFactRequest>(), Arg.Any<CancellationToken>())
trajectoryFactNnbRepositoryMock.GetByRequestAsync(Arg.Any<TrajectoryRequest>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(new[] { fakeLastFactTrajectory });
wellOperationRepositoryMock.GetAsync(Arg.Any<WellOperationRequest>(), Arg.Any<CancellationToken>())

View File

@ -0,0 +1,127 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.Trajectory.Export;
using NSubstitute;
using System;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace AsbCloudWebApi.Tests.UnitTests.Services.Trajectory
{
public class TrajectoryExportTest
{
private IWellService wellService;
private readonly ITrajectoryEditableRepository<TrajectoryGeoPlanDto> trajectoryPlanRepository;
private readonly TrajectoryPlanExportService trajectoryPlanExportService;
private readonly ITrajectoryEditableRepository<TrajectoryGeoFactDto> trajectoryFactManualReposirory;
private readonly TrajectoryFactManualExportService trajectoryFactManualExportService;
private readonly ITrajectoryNnbRepository trajectoryFactNnbRepository;
private readonly TrajectoryFactNnbExportService trajectoryFactNnbExportService;
private readonly int idWell = 4;
private readonly TrajectoryGeoPlanDto[] trajectoryPlanRows = new TrajectoryGeoPlanDto[2] {
new TrajectoryGeoPlanDto() {
Id = 1,
AzimuthGeo = 1,
AzimuthMagnetic = 2,
Comment = "комментарий",
IdUser = 1,
IdWell = 4,
Radius = 3,
UpdateDate = DateTime.Now,
VerticalDepth = 100,
WellboreDepth = 100,
ZenithAngle = 10
},
new TrajectoryGeoPlanDto() {
Id = 2,
AzimuthGeo = 1,
AzimuthMagnetic = 2,
Comment = "комментарий",
IdUser = 1,
IdWell = 4,
Radius = 3,
UpdateDate = DateTime.Now,
VerticalDepth = 100,
WellboreDepth = 100,
ZenithAngle = 10
},
};
private readonly TrajectoryGeoFactDto[] trajectoryFactRows = new TrajectoryGeoFactDto[2] {
new TrajectoryGeoFactDto() {
Id = 1,
AzimuthGeo = 1,
AzimuthMagnetic = 2,
Comment = "комментарий",
IdUser = 1,
IdWell = 4,
UpdateDate = DateTime.Now,
VerticalDepth = 100,
WellboreDepth = 100,
ZenithAngle = 10
},
new TrajectoryGeoFactDto() {
Id = 2,
AzimuthGeo = 1,
AzimuthMagnetic = 2,
Comment = "комментарий",
IdUser = 1,
IdWell = 4,
UpdateDate = DateTime.Now,
VerticalDepth = 100,
WellboreDepth = 100,
ZenithAngle = 10
},
};
public TrajectoryExportTest()
{
wellService = Substitute.For<IWellService>();
trajectoryPlanRepository = Substitute.For<ITrajectoryEditableRepository<TrajectoryGeoPlanDto>>();
trajectoryPlanExportService = new TrajectoryPlanExportService(wellService, trajectoryPlanRepository);
trajectoryFactManualReposirory = Substitute.For<ITrajectoryEditableRepository<TrajectoryGeoFactDto>>();
trajectoryFactManualExportService = new TrajectoryFactManualExportService(wellService, trajectoryFactManualReposirory);
trajectoryFactNnbRepository = Substitute.For<ITrajectoryNnbRepository>();
trajectoryFactNnbExportService = new TrajectoryFactNnbExportService(wellService, trajectoryFactNnbRepository);
}
[Fact]
public async Task Export_trajectory_plan()
{
trajectoryPlanRepository.GetAsync(idWell, CancellationToken.None)
.Returns(trajectoryPlanRows);
var stream = await trajectoryPlanExportService.ExportAsync(idWell, CancellationToken.None);
Assert.True(stream.Length > 0);
}
[Fact]
public async Task Export_trajectory_fact_manual()
{
trajectoryFactManualReposirory.GetAsync(idWell, CancellationToken.None)
.Returns(trajectoryFactRows);
var stream = await trajectoryFactManualExportService.ExportAsync(idWell, CancellationToken.None);
Assert.True(stream.Length > 0);
}
[Fact]
public async Task Export_trajectory_fact_nnb()
{
trajectoryFactNnbRepository.GetAsync(idWell, CancellationToken.None)
.Returns(trajectoryFactRows);
var stream = await trajectoryFactNnbExportService.ExportAsync(idWell, CancellationToken.None);
Assert.True(stream.Length > 0);
}
}
}

View File

@ -0,0 +1,48 @@
using AsbCloudInfrastructure.Services.Trajectory.Import;
using System.Linq;
using Xunit;
namespace AsbCloudWebApi.Tests.UnitTests.Services.Trajectory
{
public class TrajectoryImportTest
{
private readonly TrajectoryPlanParserService trajectoryPlanImportService;
private readonly TrajectoryFactManualParserService trajectoryFactManualImportService;
private string usingTemplateFile = "AsbCloudWebApi.Tests.UnitTests.Services.Trajectory.Templates";
public TrajectoryImportTest()
{
trajectoryPlanImportService = new TrajectoryPlanParserService();
trajectoryFactManualImportService = new TrajectoryFactManualParserService();
}
[Fact]
public void Import_trajectory_plan()
{
var stream = System.Reflection.Assembly.GetExecutingAssembly()
.GetManifestResourceStream($"{usingTemplateFile}.TrajectoryPlanTemplate.xlsx");
if (stream is null)
Assert.Fail("Файла для импорта не существует");
var trajectoryRows = trajectoryPlanImportService.Import(stream);
Assert.Equal(3, trajectoryRows.Count());
}
[Fact]
public void Import_trajectory_fact_manual()
{
var stream = System.Reflection.Assembly.GetExecutingAssembly()
.GetManifestResourceStream($"{usingTemplateFile}.TrajectoryFactManualTemplate.xlsx");
if (stream is null)
Assert.Fail("Файла для импорта не существует");
var trajectoryRows = trajectoryFactManualImportService.Import(stream);
Assert.Equal(4, trajectoryRows.Count());
}
}
}

View File

@ -2,7 +2,9 @@
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudInfrastructure.Services.Trajectory;
using NSubstitute;
using Xunit;
@ -11,14 +13,15 @@ namespace AsbCloudWebApi.Tests.UnitTests.Services;
public class TrajectoryVisualizationServiceTest
{
private readonly ITrajectoryPlanRepository trajectoryPlanRepositoryMock = Substitute.For<ITrajectoryPlanRepository>();
private readonly ITrajectoryFactRepository trajectoryFactRepositoryMock = Substitute.For<ITrajectoryFactRepository>();
private readonly ITrajectoryEditableRepository<TrajectoryGeoPlanDto> trajectoryPlanRepositoryMock = Substitute.For<ITrajectoryEditableRepository<TrajectoryGeoPlanDto>>();
private readonly ITrajectoryEditableRepository<TrajectoryGeoFactDto> trajectoryFactRepositoryMock = Substitute.For<ITrajectoryEditableRepository<TrajectoryGeoFactDto>>();
private readonly ITrajectoryNnbRepository trajectoryNnbRepositoryMock = Substitute.For<ITrajectoryNnbRepository>();
private readonly TrajectoryService trajectoryService;
private readonly TrajectoryService trajectoryService;
public TrajectoryVisualizationServiceTest()
{
trajectoryService = new TrajectoryService(trajectoryPlanRepositoryMock, trajectoryFactRepositoryMock);
trajectoryService = new TrajectoryService(trajectoryPlanRepositoryMock, trajectoryFactRepositoryMock, trajectoryNnbRepositoryMock);
}
[Fact]
@ -42,19 +45,31 @@ public class TrajectoryVisualizationServiceTest
new() { WellboreDepth = 0, ZenithAngle = 0, AzimuthGeo = 20 },
};
var nnbTrajectory = new TrajectoryGeoFactDto[]
{
new() { WellboreDepth = 0, ZenithAngle = 0, AzimuthGeo = 0 },
new() { WellboreDepth = 30, ZenithAngle = 30, AzimuthGeo = 10 },
new() { WellboreDepth = 0, ZenithAngle = 0, AzimuthGeo = 20 },
new() { WellboreDepth = 0, ZenithAngle = 10, AzimuthGeo = 20 },
};
trajectoryPlanRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(plannedTrajectory);
trajectoryFactRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(actualTrajectory);
//act
var result = await trajectoryService.GetTrajectoryCartesianAsync(1, CancellationToken.None);
trajectoryNnbRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(nnbTrajectory);
//act
var result = await trajectoryService.GetTrajectoryCartesianAsync(1, CancellationToken.None);
//assert
Assert.Equal(plannedTrajectory.Count(), result.Plan?.Count());
Assert.Equal(actualTrajectory.Count(), result.Fact?.Count());
}
Assert.Equal(actualTrajectory.Count(), result.FactManual?.Count());
Assert.Equal(nnbTrajectory.Count(), result.FactNnb?.Count());
}
[Fact]
public async Task GetTrajectoryAsync_ShouldReturn_StraightBore()
@ -79,28 +94,46 @@ public class TrajectoryVisualizationServiceTest
new() { WellboreDepth = 30, ZenithAngle = 0, AzimuthGeo = 0 },
new() { WellboreDepth = 50, ZenithAngle = 0, AzimuthGeo = 0 },
};
var nnbTrajectory = new TrajectoryGeoFactDto[]
{
new() { WellboreDepth = 0, ZenithAngle = 0, AzimuthGeo = 0 },
new() { WellboreDepth = 0, ZenithAngle = 0, AzimuthGeo = 0 },
new() { WellboreDepth = 20, ZenithAngle = 0, AzimuthGeo = 0 },
new() { WellboreDepth = 20, ZenithAngle = 0, AzimuthGeo = 0 },
new() { WellboreDepth = 30, ZenithAngle = 0, AzimuthGeo = 0 },
new() { WellboreDepth = 50, ZenithAngle = 0, AzimuthGeo = 0 },
};
trajectoryPlanRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(plannedTrajectory);
trajectoryFactRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(actualTrajectory);
//act
var result = await trajectoryService.GetTrajectoryCartesianAsync(1, CancellationToken.None);
trajectoryNnbRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(nnbTrajectory);
//act
var result = await trajectoryService.GetTrajectoryCartesianAsync(1, CancellationToken.None);
//assert
var lastPointPlan = result.Plan!.Last();
var lastPointFact = result.Fact!.Last();
var lastPointFact = result.FactManual!.Last();
var lastPointNnb = result.FactNnb!.Last();
Assert.Equal(0d, lastPointPlan.X, 0.1d);
Assert.Equal(0d, lastPointPlan.X, 0.1d);
Assert.Equal(-50d, lastPointPlan.Y, 0.1d);
Assert.Equal(0d, lastPointPlan.Z, 0.1d);
Assert.Equal(0d, lastPointFact.X, 0.1d);
Assert.Equal(-50d, lastPointFact.Y, 0.1d);
Assert.Equal(0d, lastPointFact.Z, 0.1d);
}
Assert.Equal(0d, lastPointNnb.X, 0.1d);
Assert.Equal(-50d, lastPointNnb.Y, 0.1d);
Assert.Equal(0d, lastPointNnb.Z, 0.1d);
}
[Fact]
public async Task GetTrajectoryAsync_ShouldReturn_Match()
@ -122,26 +155,42 @@ public class TrajectoryVisualizationServiceTest
new() { WellboreDepth = 10, ZenithAngle = 30, AzimuthGeo = 30 },
new() { WellboreDepth = 20, ZenithAngle = 0, AzimuthGeo = 0 },
};
var nnbTrajectory = new TrajectoryGeoFactDto[]
{
new() { WellboreDepth = 0, ZenithAngle = 0, AzimuthGeo = 0 },
new() { WellboreDepth = 10, ZenithAngle = 30, AzimuthGeo = 30 },
new() { WellboreDepth = 20, ZenithAngle = 0, AzimuthGeo = 0 },
};
trajectoryPlanRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(plannedTrajectory);
trajectoryFactRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(actualTrajectory);
//act
var result = await trajectoryService.GetTrajectoryCartesianAsync(1, CancellationToken.None);
trajectoryNnbRepositoryMock.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
.ReturnsForAnyArgs(nnbTrajectory);
//act
var result = await trajectoryService.GetTrajectoryCartesianAsync(1, CancellationToken.None);
//assert
var lastPointPlan = result.Plan!.Last();
var lastPointFact = result.Fact!.Last();
var lastPointFact = result.FactManual!.Last();
var lastPointNnb = result.FactManual!.Last();
Assert.InRange(lastPointPlan.Z, -10 - tolerancePlan, 0 - tolerancePlan);
Assert.InRange(lastPointPlan.Z, -10 - tolerancePlan, 0 - tolerancePlan);
Assert.InRange(lastPointPlan.Y, -20 - tolerancePlan, -10 + tolerancePlan);
Assert.InRange(lastPointPlan.X, 0 + tolerancePlan, 10 - tolerancePlan);
Assert.InRange(lastPointFact.Z, -10 - toleranceFact, 0 - toleranceFact);
Assert.InRange(lastPointFact.Y, -20 - toleranceFact, -10 + toleranceFact);
Assert.InRange(lastPointFact.X, 0 + toleranceFact, 10 - toleranceFact);
}
Assert.InRange(lastPointNnb.Z, -10 - toleranceFact, 0 - toleranceFact);
Assert.InRange(lastPointNnb.Y, -20 - toleranceFact, -10 + toleranceFact);
Assert.InRange(lastPointNnb.X, 0 + toleranceFact, 10 - toleranceFact);
}
}

View File

@ -1,42 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers;
/// <summary>
/// Фактическая траектория
/// </summary>
[Authorize]
[ApiController]
[Route("api/well/{idWell}/[controller]")]
public class FactTrajectoryController : ControllerBase
{
private readonly ITrajectoryFactRepository trajectoryFactRepository;
public FactTrajectoryController(ITrajectoryFactRepository trajectoryFactRepository)
{
this.trajectoryFactRepository = trajectoryFactRepository;
}
/// <summary>
/// Метод получения всех строк траекторий по id скважины
/// </summary>
/// <param name="idWell">Id скважины</param>
/// <param name="cancellationToken">Токен отмены операции</param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<TrajectoryGeoPlanDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetRowsAsync([FromRoute] int idWell,
CancellationToken cancellationToken)
{
var factTrajectories = await trajectoryFactRepository.GetAsync(idWell,
cancellationToken);
return Ok(factTrajectories);
}
}

View File

@ -1,72 +1,119 @@
using Microsoft.AspNetCore.Mvc;
using AsbCloudApp.Exceptions;
using AsbCloudWebApi.SignalR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
namespace AsbCloudWebApi.Controllers;
/// <summary>
/// Имитирует разные типы ответа сервера
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class MockController : ControllerBase
{
/// <summary>
/// Имитирует разные типы ответа сервера
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class MockController : ControllerBase
private readonly IServiceProvider provider;
public MockController(IServiceProvider provider)
{
/// <summary>
/// имитирует http-400
/// </summary>
[HttpGet("400")]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public IActionResult Get400([FromQuery, Required]IDictionary<string, string> args)
this.provider = provider;
}
/// <summary>
/// имитирует http-400
/// </summary>
[HttpGet("400")]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public IActionResult Get400([FromQuery, Required]IDictionary<string, string> args)
{
var errors = new Dictionary<string, string[]>();
foreach (var arg in args)
{
var errors = new Dictionary<string, string[]>();
foreach (var arg in args)
{
var countOfErrors = ((arg.Key + arg.Value).Length % 3) + 1;
var errorsText = Enumerable.Range(0, countOfErrors)
.Select(i => $"{arg.Value} не соответствует критериям проверки № {i}");
var countOfErrors = ((arg.Key + arg.Value).Length % 3) + 1;
var errorsText = Enumerable.Range(0, countOfErrors)
.Select(i => $"{arg.Value} не соответствует критериям проверки № {i}");
errors.Add(arg.Key, errorsText.ToArray());
}
if (errors.Any())
{
var problem = new ValidationProblemDetails(errors);
return BadRequest(problem);
}
else
{
var problem = new ValidationProblemDetails { Detail = "at least one argument must be provided" };
return BadRequest(problem);
}
errors.Add(arg.Key, errorsText.ToArray());
}
/// <summary>
/// имитирует http-403
/// </summary>
[HttpGet("403")]
public IActionResult Get403()
if (errors.Any())
{
return Forbid();
var problem = new ValidationProblemDetails(errors);
return BadRequest(problem);
}
/// <summary>
/// имитирует http-401
/// </summary>
[HttpGet("401")]
public IActionResult Get401()
else
{
return Unauthorized();
}
/// <summary>
/// имитирует http-500
/// </summary>
[HttpGet("500")]
public IActionResult Get500()
{
throw new System.Exception("Это тестовое исключение");
var problem = new ValidationProblemDetails { Detail = "at least one argument must be provided" };
return BadRequest(problem);
}
}
/// <summary>
/// имитирует http-403
/// </summary>
[HttpGet("403")]
public IActionResult Get403()
{
return Forbid();
}
/// <summary>
/// имитирует http-401
/// </summary>
[HttpGet("401")]
public IActionResult Get401()
{
return Unauthorized();
}
/// <summary>
/// имитирует http-500
/// </summary>
[HttpGet("500")]
public IActionResult Get500()
{
throw new System.Exception("Это тестовое исключение");
}
/// <summary>
/// имитация отправки SignalR данных
/// </summary>
/// <example>
/// </example>
/// <param name="hubName">
/// Поддерживаемые hubЫ: wellInfo, notifications, telemetry, reports
/// </param>
/// <param name="methodName">Название вызываемого на клиенте метода. Прим.:"ReceiveDataSaub". Список методов см. в swagger definition signalr</param>
/// <param name="groupName">Группа пользователей. Прим.: "well_1". Если не задана - все пользователи. Шаблон формирования групп см. описание методов в swagger definition signalr</param>
/// <param name="body">передаваемая нагрузка. (json)</param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost("signalr/hubs/{hubName}/{methodName}/{groupName}")]
[Authorize]
public async Task<IActionResult> PostAsync(string hubName, string methodName, string? groupName, object body, CancellationToken token)
{
IHubClients clients = hubName.ToLower() switch {
"wellinfo" => provider.GetRequiredService<IHubContext<NotificationHub>>().Clients,
"notifications" => provider.GetRequiredService<IHubContext<NotificationHub>>().Clients,
"telemetry" => provider.GetRequiredService<IHubContext<TelemetryHub>>().Clients,
"reports" => provider.GetRequiredService<IHubContext<ReportsHub>>().Clients,
_ => throw new ArgumentInvalidException(nameof(hubName), "hubName does not listed"),
};
IClientProxy selectedClients = string.IsNullOrEmpty(groupName)
? clients.All
: clients.Group(groupName);
await selectedClients.SendAsync(methodName, body, token);
return Ok();
}
}

View File

@ -0,0 +1,82 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.Trajectory.Export;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers.Trajectory
{
/// <summary>
/// Плановые и фактические траектории (загрузка и хранение)
/// </summary>
[ApiController]
[Authorize]
public abstract class TrajectoryController<TDto> : ControllerBase
where TDto : TrajectoryGeoDto
{
protected abstract string fileName { get; set; }
private readonly IWellService wellService;
private readonly TrajectoryExportService<TDto> trajectoryExportService;
private readonly ITrajectoryRepository<TDto> trajectoryRepository;
public TrajectoryController(IWellService wellService,
TrajectoryExportService<TDto> trajectoryExportService,
ITrajectoryRepository<TDto> trajectoryRepository)
{
this.trajectoryExportService = trajectoryExportService;
this.wellService = wellService;
this.trajectoryRepository = trajectoryRepository;
}
/// <summary>
/// Формируем excel файл с текущими строками траектории
/// </summary>
/// <param name="idWell">id скважины</param>
/// <param name="token"> Токен отмены задачи </param>
/// <returns>Запрашиваемый файл</returns>
[HttpGet("export")]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> ExportAsync([FromRoute] int idWell, CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell,
token).ConfigureAwait(false))
return Forbid();
var stream = await trajectoryExportService.ExportAsync(idWell, token);
var fileName = await trajectoryExportService.GetFileNameAsync(idWell, token);
return File(stream, "application/octet-stream", fileName);
}
/// <summary>
/// Получаем список всех строк координат траектории (для клиента)
/// </summary>
/// <param name="idWell">ключ скважины</param>
/// <param name="token"> Токен отмены задачи </param>
/// <returns>Список добавленных координат траектории</returns>
[HttpGet]
public async Task<ActionResult<IEnumerable<TDto>>> GetAsync(int idWell, CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell,
token).ConfigureAwait(false))
return Forbid();
var result = await trajectoryRepository.GetAsync(idWell, token);
return Ok(result);
}
protected async Task<bool> CanUserAccessToWellAsync(int idWell, CancellationToken token)
{
int? idCompany = User.GetCompanyId();
return idCompany is not null && await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false);
}
}
}

View File

@ -1,7 +1,8 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.Trajectory;
using AsbCloudInfrastructure.Services.Trajectory.Export;
using AsbCloudInfrastructure.Services.Trajectory.Import;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -10,64 +11,49 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers
namespace AsbCloudWebApi.Controllers.Trajectory
{
/// <summary>
/// Плановая траектория (загрузка и хранение)
/// Плановые и фактические траектории (загрузка и хранение)
/// </summary>
[Route("api/well/{idWell}/plannedTrajectory")]
[ApiController]
[Authorize]
public class PlannedTrajectoryController : ControllerBase
public abstract class TrajectoryEditableController<Tdto> : TrajectoryController<Tdto>
where Tdto : TrajectoryGeoDto
{
private readonly IWellService wellService;
private readonly IPlannedTrajectoryImportService plannedTrajectoryImportService;
private readonly ITrajectoryPlanRepository plannedTrajectoryRepository;
private readonly TrajectoryService trajectoryVisualizationService;
protected override string fileName { get; set; }
public PlannedTrajectoryController(IWellService wellService,
IPlannedTrajectoryImportService plannedTrajectoryImportService,
ITrajectoryPlanRepository plannedTrajectoryRepository,
TrajectoryService trajectoryVisualizationService)
private readonly TrajectoryParserService<Tdto> trajectoryImportService;
private readonly TrajectoryExportService<Tdto> trajectoryExportService;
private readonly ITrajectoryEditableRepository<Tdto> trajectoryRepository;
public TrajectoryEditableController(IWellService wellService,
TrajectoryParserService<Tdto> trajectoryImportService,
TrajectoryExportService<Tdto> trajectoryExportService,
ITrajectoryEditableRepository<Tdto> trajectoryRepository)
: base(
wellService,
trajectoryExportService,
trajectoryRepository)
{
this.plannedTrajectoryImportService = plannedTrajectoryImportService;
this.wellService = wellService;
this.plannedTrajectoryRepository = plannedTrajectoryRepository;
this.trajectoryVisualizationService = trajectoryVisualizationService;
this.trajectoryImportService = trajectoryImportService;
this.trajectoryExportService = trajectoryExportService;
this.trajectoryRepository = trajectoryRepository;
}
/// <summary>
/// Возвращает excel шаблон для заполнения строк плановой траектории
/// Возвращает excel шаблон для заполнения строк траектории
/// </summary>
/// <returns>Запрашиваемый файл</returns>
[HttpGet("template")]
[AllowAnonymous]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK,"application/octet-stream")]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public IActionResult GetTemplate()
{
var stream = plannedTrajectoryImportService.GetTemplateFile();
var fileName = "ЕЦП_шаблон_файла_плановая_траектория.xlsx";
return File(stream, "application/octet-stream", fileName);
}
/// <summary>
/// Формируем excel файл с текущими строками плановой траектории
/// </summary>
/// <param name="idWell">id скважины</param>
/// <param name="token"> Токен отмены задачи </param>
/// <returns>Запрашиваемый файл</returns>
[HttpGet("export")]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> ExportAsync([FromRoute] int idWell, CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell,
token).ConfigureAwait(false))
return Forbid();
var stream = await plannedTrajectoryImportService.ExportAsync(idWell, token);
var fileName = await plannedTrajectoryImportService.GetFileNameAsync(idWell, token);
var stream = trajectoryExportService.GetTemplateFile();
return File(stream, "application/octet-stream", fileName);
}
@ -102,8 +88,19 @@ namespace AsbCloudWebApi.Controllers
try
{
var result = await plannedTrajectoryImportService.ImportAsync(idWell, idUser.Value, stream, deleteBeforeImport, token);
return Ok(result);
var trajectoryRows = trajectoryImportService.Import(stream);
foreach (var row in trajectoryRows)
{
row.IdWell = idWell;
row.IdUser = idUser.Value;
}
if (deleteBeforeImport)
await trajectoryRepository.DeleteByIdWellAsync(idWell, token);
var rowsCount = await trajectoryRepository.AddRangeAsync(trajectoryRows, token);
return Ok(rowsCount);
}
catch (FileFormatException ex)
{
@ -111,22 +108,6 @@ namespace AsbCloudWebApi.Controllers
}
}
/// <summary>
/// Получаем список всех строк координат плановой траектории (для клиента)
/// </summary>
/// <param name="idWell">id скважины</param>
/// <param name="token"> Токен отмены задачи </param>
/// <returns>Список добавленных координат плановой траектории</returns>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<TrajectoryGeoPlanDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetAsync([FromRoute] int idWell, CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell,
token).ConfigureAwait(false))
return Forbid();
var result = await plannedTrajectoryRepository.GetAsync(idWell, token);
return Ok(result);
}
/// <summary>
/// Добавить одну новую строчку координат для плановой траектории
@ -137,7 +118,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns>количество успешно записанных строк в БД</returns>
[HttpPost]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> AddAsync(int idWell, [FromBody] TrajectoryGeoPlanDto row,
public async Task<IActionResult> AddAsync(int idWell, [FromBody] Tdto row,
CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
@ -147,7 +128,7 @@ namespace AsbCloudWebApi.Controllers
return Forbid();
row.IdUser = idUser.Value;
row.IdWell = idWell;
var result = await plannedTrajectoryRepository.AddAsync(row, token);
var result = await trajectoryRepository.AddAsync(row, token);
return Ok(result);
}
@ -160,7 +141,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns>количество успешно записанных строк в БД</returns>
[HttpPost("range")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> AddRangeAsync(int idWell, [FromBody] IEnumerable<TrajectoryGeoPlanDto> rows,
public async Task<IActionResult> AddRangeAsync(int idWell, [FromBody] IEnumerable<Tdto> rows,
CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
@ -173,7 +154,7 @@ namespace AsbCloudWebApi.Controllers
item.IdUser = idUser.Value;
item.IdWell = idWell;
}
var result = await plannedTrajectoryRepository.AddRangeAsync(rows, token);
var result = await trajectoryRepository.AddRangeAsync(rows, token);
return Ok(result);
}
@ -188,7 +169,7 @@ namespace AsbCloudWebApi.Controllers
[HttpPut("{idRow}")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> UpdateAsync(int idWell, int idRow,
[FromBody] TrajectoryGeoPlanDto row, CancellationToken token)
[FromBody] Tdto row, CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid();
@ -198,7 +179,7 @@ namespace AsbCloudWebApi.Controllers
row.Id = idRow;
row.IdUser = idUser.Value;
row.IdWell = idWell;
var result = await plannedTrajectoryRepository.UpdateAsync(row, token);
var result = await trajectoryRepository.UpdateAsync(row, token);
return Ok(result);
}
@ -217,35 +198,9 @@ namespace AsbCloudWebApi.Controllers
token).ConfigureAwait(false))
return Forbid();
var result = await plannedTrajectoryRepository.DeleteRangeAsync(new int[] { idRow }, token);
var result = await trajectoryRepository.DeleteRangeAsync(new int[] { idRow }, token);
return Ok(result);
}
/// <summary>
/// Получение координат для визуализации траектории (плановой и фактической)
/// </summary>
/// <param name="idWell"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("trajectoryCartesianPlanFact")]
[ProducesResponseType(typeof(PlanFactBase<IEnumerable<TrajectoryCartesianPlanDto>, IEnumerable<TrajectoryCartesianFactDto>>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetTrajectoryCartesianPlanFactAsync(int idWell, CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell,
token).ConfigureAwait(false))
return Forbid();
var result = await trajectoryVisualizationService.GetTrajectoryCartesianAsync(idWell, token);
return Ok(result);
}
private async Task<bool> CanUserAccessToWellAsync(int idWell, CancellationToken token)
{
int? idCompany = User.GetCompanyId();
return idCompany is not null && await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,30 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.Trajectory.Export;
using AsbCloudInfrastructure.Services.Trajectory.Import;
using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers.Trajectory;
/// <summary>
/// Фактическая траектория
/// </summary>
[ApiController]
[Route("api/well/{idWell}/[controller]")]
public class TrajectoryFactManualController : TrajectoryEditableController<TrajectoryGeoFactDto>
{
protected override string fileName { get; set; }
public TrajectoryFactManualController(IWellService wellService,
TrajectoryFactManualParserService factTrajectoryImportService,
TrajectoryFactManualExportService factTrajectoryExportService,
ITrajectoryEditableRepository<TrajectoryGeoFactDto> trajectoryFactRepository)
: base(
wellService,
factTrajectoryImportService,
factTrajectoryExportService,
trajectoryFactRepository)
{
fileName = "ЕЦП_шаблон_файлаактическая_траектория.xlsx";
}
}

View File

@ -0,0 +1,30 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.Trajectory.Export;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers.Trajectory;
/// <summary>
/// Фактическая траектория из ННБ
/// </summary>
[Authorize]
[ApiController]
[Route("api/well/{idWell}/[controller]")]
public class TrajectoryFactNnbController : TrajectoryController<TrajectoryGeoFactDto>
{
protected override string fileName { get; set; }
public TrajectoryFactNnbController(
ITrajectoryNnbRepository trajectoryNnbRepository,
TrajectoryFactNnbExportService trajectoryExportService,
IWellService wellService)
: base(
wellService,
trajectoryExportService,
trajectoryNnbRepository)
{
fileName = "ЕЦП_шаблон_файлаактическая_ннбраектория.xlsx";
}
}

View File

@ -0,0 +1,58 @@
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services.Trajectory;
using AsbCloudInfrastructure.Services.Trajectory.Import;
using AsbCloudInfrastructure.Services.Trajectory.Export;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers.Trajectory
{
/// <summary>
/// Плановая траектория (загрузка и хранение)
/// </summary>
[Route("api/well/{idWell}/[controller]")]
[ApiController]
public class TrajectoryPlanController : TrajectoryEditableController<TrajectoryGeoPlanDto>
{
private readonly TrajectoryService trajectoryVisualizationService;
public TrajectoryPlanController(IWellService wellService,
TrajectoryPlanParserService trajectoryPlanImportService,
TrajectoryPlanExportService trajectoryPlanExportService,
ITrajectoryEditableRepository<TrajectoryGeoPlanDto> trajectoryPlanRepository,
TrajectoryService trajectoryVisualizationService)
: base(
wellService,
trajectoryPlanImportService,
trajectoryPlanExportService,
trajectoryPlanRepository)
{
fileName = "ЕЦП_шаблон_файла_плановая_траектория.xlsx";
this.trajectoryVisualizationService = trajectoryVisualizationService;
}
/// <summary>
/// Получение координат для визуализации траектории (плановой и фактической)
/// </summary>
/// <param name="idWell"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("trajectoryCartesianPlanFact")]
[ProducesResponseType(typeof(TrajectoryPlanFactDto<IEnumerable<TrajectoryCartesianPlanDto>, IEnumerable<TrajectoryCartesianFactDto>>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetTrajectoryCartesianPlanFactAsync(int idWell, CancellationToken token)
{
if (!await CanUserAccessToWellAsync(idWell,
token).ConfigureAwait(false))
return Forbid();
var result = await trajectoryVisualizationService.GetTrajectoryCartesianAsync(idWell, token);
return Ok(result);
}
}
}

View File

@ -9,8 +9,10 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.WellOperationImport;
using AsbCloudApp.Services.WellOperationImport;
using AsbCloudApp.Data.WellOperationImport.Options;
using AsbCloudApp.Exceptions;
@ -204,37 +206,87 @@ namespace AsbCloudWebApi.Controllers
return Ok(result);
}
/// <summary>
/// Добавляет новую операцию на скважину
/// </summary>
/// <param name="idWell">Id скважины</param>
/// <param name="idType">Тип добавляемой операции</param>
/// <param name="wellOperation">Добавляемая операция</param>
/// <param name="cancellationToken"></param>
/// <returns>Количество добавленных в БД записей</returns>
[HttpPost("{idType:int}")]
[Permission]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> InsertAsync(
[Range(1, int.MaxValue, ErrorMessage = "Id скважины не может быть меньше 1")] int idWell,
[Range(0, 1, ErrorMessage = "Тип операции недопустим. Допустимые: 0, 1")] int idType,
WellOperationDto wellOperation,
CancellationToken cancellationToken)
{
if (!await CanUserAccessToWellAsync(idWell, cancellationToken))
return Forbid();
if (!await CanUserEditWellOperationsAsync(idWell, cancellationToken))
return Forbid();
wellOperation.IdWell = idWell;
wellOperation.LastUpdateDate = DateTimeOffset.UtcNow;
wellOperation.IdUser = User.GetUserId();
wellOperation.IdType = idType;
var result = await operationRepository.InsertRangeAsync(new[] { wellOperation }, cancellationToken);
return Ok(result);
}
/// <summary>
/// Добавляет новые операции на скважине
/// </summary>
/// <param name="idWell">id скважины</param>
/// <param name="values">Данные о добавляемых операциях</param>
/// <param name="token">Токен отмены задачи</param>
/// <returns>Количество добавленных в БД строк</returns>
[HttpPost]
/// <param name="idWell">Id скважины</param>
/// <param name="wellOperations">Добавляемые операции</param>
/// <param name="idType">Тип добавляемых операций</param>
/// <param name="deleteBeforeInsert">Удалить операции перед сохранением</param>
/// <param name="cancellationToken"></param>
/// <returns>Количество добавленных в БД записей</returns>
[HttpPost("{idType:int}/{deleteBeforeInsert:bool}")]
[Permission]
[ProducesResponseType(typeof(IEnumerable<WellOperationDto>), (int)System.Net.HttpStatusCode.OK)]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> InsertRangeAsync(
[Range(1, int.MaxValue, ErrorMessage = "Id скважины не может быть меньше 1")] int idWell,
[FromBody] IEnumerable<WellOperationDto> values,
CancellationToken token)
[Range(1, int.MaxValue, ErrorMessage = "Id скважины не может быть меньше 1")] int idWell,
[Range(0, 1, ErrorMessage = "Тип операции недопустим. Допустимые: 0, 1")] int idType,
bool deleteBeforeInsert,
[FromBody] IEnumerable<WellOperationDto> wellOperations,
CancellationToken cancellationToken)
{
if (!await CanUserAccessToWellAsync(idWell, token))
return Forbid();
if (!await CanUserEditWellOperationsAsync(idWell, token))
if (!await CanUserAccessToWellAsync(idWell, cancellationToken))
return Forbid();
foreach (var value in values)
if (!await CanUserEditWellOperationsAsync(idWell, cancellationToken))
return Forbid();
if (deleteBeforeInsert && wellOperations.Any())
{
value.IdWell = idWell;
value.LastUpdateDate = DateTimeOffset.UtcNow;
value.IdUser = User.GetUserId();
var existingOperations = await operationRepository.GetAsync(new WellOperationRequest
{
IdWell = idWell,
OperationType = idType
}, cancellationToken);
await operationRepository.DeleteAsync(existingOperations.Select(o => o.Id), cancellationToken);
}
foreach (var wellOperation in wellOperations)
{
wellOperation.IdWell = idWell;
wellOperation.LastUpdateDate = DateTimeOffset.UtcNow;
wellOperation.IdUser = User.GetUserId();
wellOperation.IdType = idType;
}
var result = await operationRepository.InsertRangeAsync(values, token)
.ConfigureAwait(false);
var result = await operationRepository.InsertRangeAsync(wellOperations, cancellationToken);
return Ok(result);
}
@ -299,46 +351,19 @@ namespace AsbCloudWebApi.Controllers
/// <param name="idWell">id скважины</param>
/// <param name="options">Параметры для парсинга файла</param>
/// <param name="files">Коллекция из одного файла xlsx</param>
/// <param name="deleteBeforeImport">Удалить операции перед импортом = 1, если файл валидный</param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpPost("import/default/{deleteBeforeImport}")]
[HttpPost("import/default")]
[ProducesResponseType(typeof(IEnumerable<WellOperationDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[Permission]
public async Task<IActionResult> ImportDefaultExcelFileAsync(int idWell,
public Task<IActionResult> ImportDefaultExcelFileAsync(int idWell,
[FromQuery] WellOperationImportDefaultOptionsDto options,
[FromForm] IFormFileCollection files,
[Range(0, 1, ErrorMessage = "Недопустимое значение. Допустимые: 0, 1")] int deleteBeforeImport,
CancellationToken token)
{
var idUser = User.GetUserId();
if (!idUser.HasValue)
throw new ForbidException("Неизвестный пользователь");
await AssertUserHasAccessToImportWellOperationsAsync(idWell, token);
if (files.Count < 1)
return this.ValidationBadRequest(nameof(files), "Нет файла");
var file = files[0];
if (Path.GetExtension(file.FileName).ToLower() != ".xlsx")
return this.ValidationBadRequest(nameof(files), "Требуется xlsx файл.");
using Stream stream = file.OpenReadStream();
try
{
var sheet = wellOperationDefaultExcelParser.Parse(stream, options);
await wellOperationImportService.ImportAsync(idWell, idUser.Value, options.IdType, sheet, (deleteBeforeImport & 1) > 0, token);
}
catch (FileFormatException ex)
{
return this.ValidationBadRequest(nameof(files), ex.Message);
}
return Ok();
}
CancellationToken cancellationToken) => ImportExcelFileAsync(idWell, files, options,
(stream, _) => wellOperationDefaultExcelParser.Parse(stream, options),
cancellationToken);
/// <summary>
/// Импорт операций из excel (xlsx) файла. ГПНХ (Хантос)
@ -346,46 +371,19 @@ namespace AsbCloudWebApi.Controllers
/// <param name="idWell">id скважины</param>
/// <param name="options">Параметры для парсинга файла</param>
/// <param name="files">Коллекция из одного файла xlsx</param>
/// <param name="deleteBeforeImport">Удалить операции перед импортом = 1, если файл валидный</param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpPost("import/gazpromKhantos/{deleteBeforeImport}")]
[HttpPost("import/gazpromKhantos")]
[ProducesResponseType(typeof(IEnumerable<WellOperationDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[Permission]
public async Task<IActionResult> ImportGazpromKhantosExcelFileAsync(int idWell,
[FromQuery] WellOperationImportGazpromKhantosOptionsDto options,
[FromForm] IFormFileCollection files,
[Range(0, 1, ErrorMessage = "Недопустимое значение. Допустимые: 0, 1")] int deleteBeforeImport,
CancellationToken token)
{
var idUser = User.GetUserId();
if (!idUser.HasValue)
throw new ForbidException("Неизвестный пользователь");
await AssertUserHasAccessToImportWellOperationsAsync(idWell, token);
if (files.Count < 1)
return this.ValidationBadRequest(nameof(files), "Нет файла");
var file = files[0];
if (Path.GetExtension(file.FileName).ToLower() != ".xlsx")
return this.ValidationBadRequest(nameof(files), "Требуется xlsx файл.");
using Stream stream = file.OpenReadStream();
try
{
var sheet = wellOperationGazpromKhantosExcelParser.Parse(stream, options);
await wellOperationImportService.ImportAsync(idWell, idUser.Value, options.IdType, sheet, (deleteBeforeImport & 1) > 0, token);
}
catch (FileFormatException ex)
{
return this.ValidationBadRequest(nameof(files), ex.Message);
}
return Ok();
}
public Task<IActionResult> ImportGazpromKhantosExcelFileAsync(int idWell,
[FromQuery] WellOperationImportGazpromKhantosOptionsDto options,
[FromForm] IFormFileCollection files,
CancellationToken cancellationToken) => ImportExcelFileAsync(idWell, files, options,
(stream, _) => wellOperationGazpromKhantosExcelParser.Parse(stream, options),
cancellationToken);
/// <summary>
/// Создает excel файл с операциями по скважине
@ -453,7 +451,11 @@ namespace AsbCloudWebApi.Controllers
return File(stream, "application/octet-stream", fileName);
}
private async Task AssertUserHasAccessToImportWellOperationsAsync(int idWell, CancellationToken token)
private async Task<IActionResult> ImportExcelFileAsync<TOptions>(int idWell, [FromForm] IFormFileCollection files,
TOptions options,
Func<Stream, TOptions, SheetDto> parseMethod,
CancellationToken cancellationToken)
where TOptions : IWellOperationImportOptions
{
var idCompany = User.GetCompanyId();
var idUser = User.GetUserId();
@ -461,16 +463,54 @@ namespace AsbCloudWebApi.Controllers
if (!idCompany.HasValue || !idUser.HasValue)
throw new ForbidException("Неизвестный пользователь");
if (!await CanUserAccessToWellAsync(idWell, token))
if (!await CanUserAccessToWellAsync(idWell, cancellationToken))
throw new ForbidException("Нет доступа к скважине");
if (!await CanUserEditWellOperationsAsync(idWell, token))
if (!await CanUserEditWellOperationsAsync(idWell, cancellationToken))
throw new ForbidException("Недостаточно прав для редактирования ГГД на завершенной скважине");
if (!await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, token))
if (!await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, cancellationToken))
throw new ForbidException("Скважина недоступна для компании");
}
if (files.Count < 1)
return this.ValidationBadRequest(nameof(files), "Нет файла");
var file = files[0];
if (Path.GetExtension(file.FileName).ToLower() != ".xlsx")
return this.ValidationBadRequest(nameof(files), "Требуется xlsx файл.");
using Stream stream = file.OpenReadStream();
try
{
var sheet = parseMethod(stream, options);
var wellOperations = wellOperationImportService.Import(idWell, idUser.Value, options.IdType, sheet)
.OrderBy(w => w.DateStart);
var dateStart = wellOperations.Min(w => w.DateStart);
foreach (var wellOperation in wellOperations)
wellOperation.Day = (wellOperation.DateStart - dateStart).TotalDays;
if (!wellOperations.Any())
return NoContent();
return Ok(wellOperations);
}
catch (FileFormatException ex)
{
return this.ValidationBadRequest(nameof(files), ex.Message);
}
}
private async Task<bool> CanUserAccessToWellAsync(int idWell, CancellationToken token)
{
int? idCompany = User.GetCompanyId();
return idCompany is not null && await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false);
}
private async Task<bool> CanUserEditWellOperationsAsync(int idWell, CancellationToken token)
{
var idUser = User.GetUserId();
@ -485,13 +525,5 @@ namespace AsbCloudWebApi.Controllers
return well.IdState != 2 || userRepository.HasPermission(idUser.Value, "WellOperation.editCompletedWell");
}
private async Task<bool> CanUserAccessToWellAsync(int idWell, CancellationToken token)
{
int? idCompany = User.GetCompanyId();
return idCompany is not null && await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false);
}
}
}
}