Merge branch 'dev' into fix/add-sort-to-paginated-elements

This commit is contained in:
Никита Фролов 2023-02-03 09:57:05 +05:00
commit 71bb74137b
30 changed files with 14707 additions and 317 deletions

View File

@ -63,6 +63,11 @@ namespace AsbCloudApp.Data.SAUB
/// </summary> /// </summary>
public float PressureDeltaLimitMax { get; set; } public float PressureDeltaLimitMax { get; set; }
/// <summary>
/// Перепад давления
/// </summary>
public float PressureDelta { get; set; }
/// <summary> /// <summary>
/// осевая нагрузка /// осевая нагрузка
/// </summary> /// </summary>

View File

@ -0,0 +1,51 @@
using AsbCloudApp.Data;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Repositories
{
#nullable enable
/// <summary>
/// Репозиторий "Дело скважины"
/// </summary>
public interface IWellFinalDocumentsRepository
{
/// <summary>
/// Обновление всех записей по скважине
/// </summary>
/// <param name="idWell"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<WellFinalDocumentDBDto>> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto>? dtos, CancellationToken token);
/// <summary>
/// Получение всех записей
/// </summary>
/// <param name="idWell"></param>
/// <param name="idUser"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<WellCaseDto> GetByWellIdAsync(int idWell, int idUser, CancellationToken token);
/// <summary>
/// Получение списка ответственных
/// </summary>
/// <param name="idWell"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<UserDto>> GetAvailableUsersAsync(int idWell, CancellationToken token);
/// <summary>
/// Возвращаент категорию файла
/// </summary>
/// <param name="idWell"></param>
/// <param name="idCategory"></param>
/// <param name="idUser"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<WellFinalDocumentDBDto> GetCategoryAsync(int idWell, int idCategory, int idUser, CancellationToken token);
}
#nullable disable
}

View File

@ -21,23 +21,6 @@ namespace AsbCloudApp.Services
/// <returns></returns> /// <returns></returns>
Task<int> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto>? dtos, CancellationToken token); Task<int> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto>? dtos, CancellationToken token);
/// <summary>
/// Получение всех записей
/// </summary>
/// <param name = "idWell" ></param >
/// <param name = "idUser" >запрашивающий пользователь, для проверки его прав и текста сообщения</param >
/// <param name="token"></param>
/// <returns></returns>
Task<WellCaseDto> GetByWellIdAsync(int idWell, int idUser, CancellationToken token);
/// <summary>
/// Получение списка ответственных
/// </summary>
/// <param name="idWell"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<UserDto>> GetAvailableUsersAsync(int idWell, CancellationToken token);
/// <summary> /// <summary>
/// Получение истории файлов /// Получение истории файлов
/// </summary> /// </summary>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Fix_PlannedTrajectory_permissions : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.UpdateData(
table: "t_permission",
keyColumn: "id",
keyValue: 510,
column: "name",
value: "PlannedTrajectory.get");
migrationBuilder.UpdateData(
table: "t_permission",
keyColumn: "id",
keyValue: 511,
column: "name",
value: "PlannedTrajectory.edit");
migrationBuilder.UpdateData(
table: "t_permission",
keyColumn: "id",
keyValue: 512,
column: "name",
value: "PlannedTrajectory.delete");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.UpdateData(
table: "t_permission",
keyColumn: "id",
keyValue: 510,
column: "name",
value: "TelemetryWirelineRunOut.get");
migrationBuilder.UpdateData(
table: "t_permission",
keyColumn: "id",
keyValue: 511,
column: "name",
value: "TelemetryWirelineRunOut.edit");
migrationBuilder.UpdateData(
table: "t_permission",
keyColumn: "id",
keyValue: 512,
column: "name",
value: "TelemetryWirelineRunOut.delete");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_permissions_for_ProcessMap : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "t_permission",
columns: new[] { "id", "description", "name" },
values: new object[,]
{
{ 513, "Разрешение просматривать РТК", "ProcessMap.get" },
{ 514, "Разрешение редактировать РТК", "ProcessMap.edit" },
{ 515, "Разрешение удалять РТК", "ProcessMap.delete" }
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "t_permission",
keyColumn: "id",
keyValue: 513);
migrationBuilder.DeleteData(
table: "t_permission",
keyColumn: "id",
keyValue: 514);
migrationBuilder.DeleteData(
table: "t_permission",
keyColumn: "id",
keyValue: 515);
}
}
}

View File

@ -1770,19 +1770,37 @@ namespace AsbCloudDb.Migrations
{ {
Id = 510, Id = 510,
Description = "Разрешение просматривать плановая траектория", Description = "Разрешение просматривать плановая траектория",
Name = "TelemetryWirelineRunOut.get" Name = "PlannedTrajectory.get"
}, },
new new
{ {
Id = 511, Id = 511,
Description = "Разрешение редактировать плановая траектория", Description = "Разрешение редактировать плановая траектория",
Name = "TelemetryWirelineRunOut.edit" Name = "PlannedTrajectory.edit"
}, },
new new
{ {
Id = 512, Id = 512,
Description = "Разрешение удалять плановая траектория", Description = "Разрешение удалять плановая траектория",
Name = "TelemetryWirelineRunOut.delete" Name = "PlannedTrajectory.delete"
},
new
{
Id = 513,
Description = "Разрешение просматривать РТК",
Name = "ProcessMap.get"
},
new
{
Id = 514,
Description = "Разрешение редактировать РТК",
Name = "ProcessMap.edit"
},
new
{
Id = 515,
Description = "Разрешение удалять РТК",
Name = "ProcessMap.delete"
}); });
}); });

View File

@ -138,9 +138,16 @@
new (){ Id = 507, Name="TelemetryWirelineRunOut.get", Description="Разрешение просматривать наработка талевого каната"}, new (){ Id = 507, Name="TelemetryWirelineRunOut.get", Description="Разрешение просматривать наработка талевого каната"},
new (){ Id = 510, Name="PlannedTrajectory.get", Description="Разрешение просматривать плановая траектория"},
new (){ Id = 511, Name="PlannedTrajectory.edit", Description="Разрешение редактировать плановая траектория"},
new (){ Id = 512, Name="PlannedTrajectory.delete", Description="Разрешение удалять плановая траектория"},
new (){ Id = 510, Name="TelemetryWirelineRunOut.get", Description="Разрешение просматривать плановая траектория"}, new (){ Id = 510, Name="TelemetryWirelineRunOut.get", Description="Разрешение просматривать плановая траектория"},
new (){ Id = 511, Name="TelemetryWirelineRunOut.edit", Description="Разрешение редактировать плановая траектория"}, new (){ Id = 511, Name="TelemetryWirelineRunOut.edit", Description="Разрешение редактировать плановая траектория"},
new (){ Id = 512, Name="TelemetryWirelineRunOut.delete", Description="Разрешение удалять плановая траектория"}, new (){ Id = 512, Name="TelemetryWirelineRunOut.delete", Description="Разрешение удалять плановая траектория"},
new (){ Id = 513, Name="ProcessMap.get", Description="Разрешение просматривать РТК"},
new (){ Id = 514, Name="ProcessMap.edit", Description="Разрешение редактировать РТК"},
new (){ Id = 515, Name="ProcessMap.delete", Description="Разрешение удалять РТК"},
}; };
} }
} }

View File

@ -37,9 +37,11 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.0" />
<PackageReference Include="ClosedXML" Version="0.96.0" /> <PackageReference Include="ClosedXML" Version="0.96.0" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" /> <PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
<PackageReference Include="itext7" Version="7.2.3" /> <PackageReference Include="itext7" Version="7.2.3" />
<PackageReference Include="iTextSharp" Version="5.5.13.3" />
<PackageReference Include="Mapster" Version="7.3.0" /> <PackageReference Include="Mapster" Version="7.3.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.23.1" /> <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.23.1" />

View File

@ -176,7 +176,8 @@ namespace AsbCloudInfrastructure
services.AddTransient<IUserRepository, UserRepository>(); services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ILimitingParameterRepository, LimitingParameterRepository>(); services.AddTransient<ILimitingParameterRepository, LimitingParameterRepository>();
services.AddTransient<ITelemetryWirelineRunOutRepository, TelemetryWirelineRunOutRepository>(); services.AddTransient<ITelemetryWirelineRunOutRepository, TelemetryWirelineRunOutRepository>();
services.AddTransient<IWellFinalDocumentsRepository, WellFinalDocumentsRepository>();
// Subsystem service // Subsystem service
services.AddTransient<ICrudRepository<SubsystemDto>, CrudCacheRepositoryBase<SubsystemDto, Subsystem>>(); services.AddTransient<ICrudRepository<SubsystemDto>, CrudCacheRepositoryBase<SubsystemDto, Subsystem>>();
services.AddTransient<ISubsystemService, SubsystemService>(); services.AddTransient<ISubsystemService, SubsystemService>();

View File

@ -0,0 +1,143 @@
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository
{
#nullable enable
public class WellFinalDocumentsRepository : IWellFinalDocumentsRepository
{
private readonly IAsbCloudDbContext context;
private readonly FileService fileService;
private readonly IUserRepository userRepository;
public WellFinalDocumentsRepository(IAsbCloudDbContext context,
FileService fileService,
IUserRepository userRepository)
{
this.context = context;
this.fileService = fileService;
this.userRepository = userRepository;
}
///<inheritdoc/>
public async Task<IEnumerable<WellFinalDocumentDBDto>> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto>? dtos, CancellationToken token)
{
if (dtos is null)
throw new ArgumentInvalidException("Данные по категориям отсутствуют.");
var entities = dtos
.Where(dto => dto.IdsPublishers?.Any() == true)
.SelectMany(dto => dto.IdsPublishers
.Select(idUser => new WellFinalDocument
{
IdCategory = dto.IdCategory,
IdWell = idWell,
IdUser = idUser
}));
var itemsToDelete = context.WellFinalDocuments.Where(d => d.IdWell == idWell);
context.WellFinalDocuments.RemoveRange(itemsToDelete);
context.WellFinalDocuments.AddRange(entities);
await context.SaveChangesAsync(token).ConfigureAwait(false);
return entities.Adapt<IEnumerable<WellFinalDocumentDBDto>>();
}
///<inheritdoc/>
public async Task<WellCaseDto> GetByWellIdAsync(int idWell, int idUser, CancellationToken token)
{
var entities = await context.WellFinalDocuments
.Include(d => d.Category)
.Include(d => d.User)
.Where(d => d.IdWell == idWell)
.AsNoTracking()
.ToArrayAsync(token)
.ConfigureAwait(false);
var entitiesGroups = entities
.GroupBy(d => d.IdCategory);
var categoriesIds = entitiesGroups
.Select(g => g.Key);
var files = (await fileService
.GetInfosAsync(new FileRequest { IdWell = idWell }, token)
.ConfigureAwait(false))
.Where(f => categoriesIds.Contains(f.IdCategory))
.ToArray();
var docs = entitiesGroups.Select((g) => new WellFinalDocumentDto
{
IdCategory = g.Key,
FilesCount = files
.Where(f => f.IdCategory == g.Key)
.Count(),
File = files
.Where(f => f.IdCategory == g.Key)
.OrderBy(f => f.UploadDate)
.LastOrDefault(),
NameCategory = g.First().Category.Name,
Publishers = g.Select(i => i.User.Adapt<UserDto>()),
PermissionToUpload = g.Any(i => i.IdUser == idUser),
});
var result = new WellCaseDto
{
IdWell = idWell,
PermissionToSetPubliher = userRepository.HasPermission(idUser, "WellFinalDocuments.editPublisher"),
WellFinalDocuments = docs,
};
return result;
}
///<inheritdoc/>
public async Task<IEnumerable<UserDto>> GetAvailableUsersAsync(int idWell, CancellationToken token)
{
var companyIds = await context.RelationCompaniesWells
.AsNoTracking()
.Where(x => x.IdWell == idWell).Select(x => x.IdCompany)
.ToListAsync(token)
.ConfigureAwait(false);
var allUsers = await userRepository
.GetAllAsync(token)
.ConfigureAwait(false);
return allUsers.Where(x => x.IdCompany is not null && companyIds.Contains(x.IdCompany ?? int.MinValue))
.OrderBy(x => x.Surname)
.Select(u => u as UserDto)
.ToArray();
}
///<inheritdoc/>
public async Task<WellFinalDocumentDBDto> GetCategoryAsync(int idWell, int idCategory, int idUser, CancellationToken token)
{
var entity = await context.WellFinalDocuments
.AsNoTracking()
.FirstOrDefaultAsync(x => x.IdWell == idWell && x.IdCategory == idCategory && x.IdUser == idUser, token);
if (entity is null)
throw new ArgumentInvalidException("Пользователь не является ответственным за загрузку файла для данной категории.");
var dto = Convert(entity);
return dto;
}
private static WellFinalDocumentDBDto Convert(WellFinalDocument entity)
=> entity.Adapt<WellFinalDocumentDBDto>();
}
#nullable disable
}

View File

@ -0,0 +1,93 @@
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CliWrap;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace AsbCloudInfrastructure.Services.DrillingProgram.Convert
{
#nullable enable
sealed internal class ConvertToPdf
{
internal static readonly string[] filesExtensions = { ".xlsx", ".xls", ".ods", ".odt", ".doc", ".docx", ".pdf" };
private static void MergeFiles(IEnumerable<string> inputFiles, string outFile)
{
using var stream = new FileStream(outFile, FileMode.Create);
using var doc = new Document();
using var pdf = new PdfCopy(doc, stream);
doc.Open();
var inputFilesList = inputFiles.ToList();
foreach (var file in inputFilesList)
{
var reader = new PdfReader(file);
for (int i = 0; i < reader.NumberOfPages; i++)
{
pdf.AddPage(pdf.GetImportedPage(reader, i + 1));
}
pdf.FreeReader(reader);
reader.Close();
};
}
private static (string programFile, string programArg) GetOptionsStartupProcess (string inputFileName, string resultFileDir)
{
(string programFile, string programArg) startupOptions;
if (OperatingSystem.IsWindows())
{
startupOptions.programFile = "C:\\Program Files\\LibreOffice\\program\\soffice.exe";
startupOptions.programArg = $"-headless -convert-to pdf {inputFileName} --outdir {resultFileDir}";
return startupOptions;
}
if(OperatingSystem.IsLinux())
{
startupOptions.programFile = "/usr/bin/soffice";
startupOptions.programArg = $"--headless --convert-to pdf {inputFileName} --outdir {resultFileDir}";
return (startupOptions);
}
throw new NotSupportedException ("Вызов процесса в текущей операционной системе не возможен");
}
private static async Task StartConvertProcessAsync(string inputFileName, string resultFileDir, CancellationToken token)
{
var (programFile, programArg) = GetOptionsStartupProcess(inputFileName, resultFileDir);
var command = Cli.Wrap(programFile)
.WithArguments(programArg);
await command.ExecuteAsync(token);
}
public static async Task GetConverteAndMergedFileAsync(IEnumerable<string> files, string resultPath, string convertedFilesDir, CancellationToken token)
{
var badFiles = files.Where(f => !filesExtensions.Contains(Path.GetExtension(f)));
if (badFiles.Any())
{
throw new FileFormatException($"Файлы: {string.Join(", ", badFiles)} - неподдерживаемого формата. " +
$"Они не могут быть добавлены в список файлов для конвертации и слияния в общий файл программы бурения.");
}
var listFiles = files
.Distinct()
.Select(f => new
{
inputFile = f,
convertedFile = Path.Combine(convertedFilesDir, "pdf", Path.ChangeExtension(Path.GetFileName(f), ".pdf"))
})
.ToList();
foreach (var file in listFiles)
{
var fileExt = Path.GetExtension(file.inputFile).ToLower();
if (fileExt != ".pdf")
{
await StartConvertProcessAsync(file.inputFile, Path.GetDirectoryName(file.convertedFile)!, token);
}
}
MergeFiles(listFiles.Select(c => c.convertedFile), resultPath);
Directory.Delete(Path.Combine(convertedFilesDir, "pdf"), true);
}
}
#nullable disable
}

View File

@ -0,0 +1,17 @@
КЛАСС ПРЕОБРАЗУЮЩИЙ ЧАСТИ ПРОГРАММЫ БУРЕНИЯ В ЕДИНЫЙ ФАЙЛ ПЕЧАТНОГО ФОРМАТА (pdf)
1. На Linux сервер необходимо установить пакеты LibreOffice:
sudo apt-get install libreoffice-writer libreoffice-calc
2. путь до бинарника LibreOffice:
Linux - /usr/bin/soffice
Windows - C:\Program Files\LibreOffice\program\soffice.exe
3. В массиве fileExtensions содержатся в виде стринг переменных необходимые расширения файлов
изначально обозначенные в задаче.
При необходимости список можно расширить.

View File

@ -4,6 +4,7 @@ using AsbCloudApp.Repositories;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Services.DrillingProgram.Convert;
using Mapster; using Mapster;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -28,7 +29,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly IConfiguration configuration; private readonly IConfiguration configuration;
private readonly BackgroundWorker backgroundWorker; private readonly BackgroundWorker backgroundWorker;
private readonly IEmailService emailService; private readonly IEmailService emailService;
private const int idFileCategoryDrillingProgram = 1000; private const int idFileCategoryDrillingProgram = 1000;
private const int idFileCategoryDrillingProgramPartsStart = 1001; private const int idFileCategoryDrillingProgramPartsStart = 1001;
@ -50,6 +51,8 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
private const int idStateReady = 3; private const int idStateReady = 3;
private const int idStateError = 4; private const int idStateError = 4;
private static readonly string[] validFileExtensions = ConvertToPdf.filesExtensions;
public DrillingProgramService( public DrillingProgramService(
IAsbCloudDbContext context, IAsbCloudDbContext context,
FileService fileService, FileService fileService,
@ -161,12 +164,21 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
return state; return state;
} }
public async Task<int> AddFile(int idWell, int idFileCategory, int idUser, string fileFullName, System.IO.Stream fileStream, CancellationToken token = default) private static bool IsFileExtensionValid(string file)
{ {
var fileExt = Path.GetExtension(file).ToLower();
return validFileExtensions.Contains(fileExt);
}
public async Task<int> AddFile(int idWell, int idFileCategory, int idUser, string fileFullName, Stream fileStream, CancellationToken token = default)
{
if (!IsFileExtensionValid(fileFullName))
throw new FileFormatException($"Файл {fileFullName} - неподдерживаемого формата. Файл не может быть загружен.");
var part = await context.DrillingProgramParts var part = await context.DrillingProgramParts
.Include(p => p.RelatedUsers) .Include(p => p.RelatedUsers)
.ThenInclude(r => r.User) .ThenInclude(r => r.User)
.FirstOrDefaultAsync(p => p.IdWell == idWell && p.IdFileCategory == idFileCategory, token); .FirstOrDefaultAsync(p => p.IdWell == idWell && p.IdFileCategory == idFileCategory, token);
if (part == null) if (part == null)
throw new ArgumentInvalidException($"DrillingProgramPart id == {idFileCategory} does not exist", nameof(idFileCategory)); throw new ArgumentInvalidException($"DrillingProgramPart id == {idFileCategory} does not exist", nameof(idFileCategory));
@ -333,9 +345,6 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
await NotifyPublisherOnFullAccepAsync(fileMarkDto, token); await NotifyPublisherOnFullAccepAsync(fileMarkDto, token);
} }
} }
return result; return result;
} }
@ -472,15 +481,15 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
if (!backgroundWorker.Contains(workId)) if (!backgroundWorker.Contains(workId))
{ {
var well = (await wellService.GetOrDefaultAsync(idWell, token))!; var well = (await wellService.GetOrDefaultAsync(idWell, token))!;
var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.xlsx"; var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.pdf";
var tempResultFilePath = Path.Combine(Path.GetTempPath(), "drillingProgram", resultFileName); var convertedFilesDir = Path.Combine(Path.GetTempPath(), "drillingProgram", $"{well.Cluster}_{well.Caption}");
var tempResultFilePath = Path.Combine(convertedFilesDir, resultFileName);
var workAction = async (string workId, IServiceProvider serviceProvider, CancellationToken token) => var workAction = async (string workId, IServiceProvider serviceProvider, CancellationToken token) =>
{ {
var context = serviceProvider.GetRequiredService<IAsbCloudDbContext>(); var context = serviceProvider.GetRequiredService<IAsbCloudDbContext>();
var fileService = serviceProvider.GetRequiredService<FileService>(); var fileService = serviceProvider.GetRequiredService<FileService>();
var files = state.Parts.Select(p => fileService.GetUrl(p.File)); var files = state.Parts.Select(p => fileService.GetUrl(p.File));
DrillingProgramMaker.UniteExcelFiles(files, tempResultFilePath, state.Parts, well); await ConvertToPdf.GetConverteAndMergedFileAsync(files, tempResultFilePath, convertedFilesDir, token);
await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram, resultFileName, tempResultFilePath, token); await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram, resultFileName, tempResultFilePath, token);
}; };

View File

@ -181,6 +181,7 @@ namespace AsbCloudInfrastructure.Services.ProcessMap
result.PressureSp += item.PressureSp * itemWeight; result.PressureSp += item.PressureSp * itemWeight;
result.PressureSpRotor += item.PressureSpSlide * itemWeight; result.PressureSpRotor += item.PressureSpSlide * itemWeight;
result.PressureIdle += item.PressureIdle * itemWeight; result.PressureIdle += item.PressureIdle * itemWeight;
result.PressureDelta += item.PressureDelta * itemWeight;
result.AxialLoad += item.AxialLoad * itemWeight; result.AxialLoad += item.AxialLoad * itemWeight;
result.AxialLoadSp += item.AxialLoadSp * itemWeight; result.AxialLoadSp += item.AxialLoadSp * itemWeight;
@ -227,7 +228,7 @@ namespace AsbCloudInfrastructure.Services.ProcessMap
if (telemetryDataStat is not null) if (telemetryDataStat is not null)
{ {
dto.PressureDiff.SetpointFact = telemetryDataStat.PressureSp; dto.PressureDiff.SetpointFact = telemetryDataStat.PressureSp;
dto.PressureDiff.Fact = telemetryDataStat.Pressure; dto.PressureDiff.Fact = telemetryDataStat.PressureDelta;
dto.PressureDiff.Limit = telemetryDataStat.PressureDeltaLimitMax; dto.PressureDiff.Limit = telemetryDataStat.PressureDeltaLimitMax;
dto.AxialLoad.SetpointFact = telemetryDataStat.AxialLoadSp; dto.AxialLoad.SetpointFact = telemetryDataStat.AxialLoadSp;

View File

@ -53,6 +53,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
PressureSpSlide = g.Average(t => t.PressureSpSlide!.Value), PressureSpSlide = g.Average(t => t.PressureSpSlide!.Value),
PressureIdle = g.Average(t => t.PressureIdle!.Value), PressureIdle = g.Average(t => t.PressureIdle!.Value),
PressureDeltaLimitMax = g.Average(t => t.PressureDeltaLimitMax!.Value), PressureDeltaLimitMax = g.Average(t => t.PressureDeltaLimitMax!.Value),
PressureDelta = g.Average(t => t.Pressure!.Value - t.PressureIdle!.Value),
AxialLoad = g.Average(t => t.AxialLoad!.Value), AxialLoad = g.Average(t => t.AxialLoad!.Value),
AxialLoadSp = g.Average(t => t.AxialLoadSp!.Value), AxialLoadSp = g.Average(t => t.AxialLoadSp!.Value),

View File

@ -94,8 +94,8 @@ namespace AsbCloudInfrastructure.Services.Subsystems
var detectedOperationsRequest = new DetectedOperationRequest() var detectedOperationsRequest = new DetectedOperationRequest()
{ {
IdWell = request.IdWell, IdWell = request.IdWell,
IdsCategories = new List<int>() { IdsCategories = new int[] {
1,3 WellOperationCategory.IdRotor, WellOperationCategory.IdSlide,
}, },
LtDate = request.LtDate, LtDate = request.LtDate,
GtDate = request.GtDate, GtDate = request.GtDate,
@ -158,9 +158,9 @@ namespace AsbCloudInfrastructure.Services.Subsystems
} }
private static (double depthIntervalRotor, double depthIntervalSlide) GetDepthInterval (IEnumerable<DetectedOperationDto> detectedOperations) private static (double depthIntervalRotor, double depthIntervalSlide) GetDepthInterval (IEnumerable<DetectedOperationDto> detectedOperations)
{ {
var depthIntervalRotor = detectedOperations.Where(o => o.IdCategory == 1) var depthIntervalRotor = detectedOperations.Where(o => o.IdCategory == WellOperationCategory.IdRotor)
.Sum(o => o.DepthEnd - o.DepthStart); .Sum(o => o.DepthEnd - o.DepthStart);
var depthIntervalSlide = detectedOperations.Where(o => o.IdCategory == 3) var depthIntervalSlide = detectedOperations.Where(o => o.IdCategory == WellOperationCategory.IdSlide)
.Sum(o => o.DepthEnd - o.DepthStart); .Sum(o => o.DepthEnd - o.DepthStart);
var depthInterval = (depthIntervalRotor, depthIntervalSlide); var depthInterval = (depthIntervalRotor, depthIntervalSlide);

View File

@ -3,11 +3,7 @@ using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -22,146 +18,54 @@ namespace AsbCloudInfrastructure.Services
/// </summary> /// </summary>
public class WellFinalDocumentsService : IWellFinalDocumentsService public class WellFinalDocumentsService : IWellFinalDocumentsService
{ {
private readonly IAsbCloudDbContext context;
private readonly FileService fileService; private readonly FileService fileService;
private readonly IUserRepository userRepository; private readonly IUserRepository userRepository;
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly IConfiguration configuration; private readonly IConfiguration configuration;
private readonly IEmailService emailService; private readonly IEmailService emailService;
private readonly IFileCategoryService fileCategoryService; private readonly IFileCategoryService fileCategoryService;
private readonly IWellFinalDocumentsRepository wellFinalDocumentsRepository;
private const int FileServiceThrewException = -1; private const int FileServiceThrewException = -1;
public WellFinalDocumentsService(IAsbCloudDbContext context, public WellFinalDocumentsService(FileService fileService,
FileService fileService,
IUserRepository userRepository, IUserRepository userRepository,
IWellService wellService, IWellService wellService,
IConfiguration configuration, IConfiguration configuration,
IEmailService emailService, IEmailService emailService,
IFileCategoryService fileCategoryService) IFileCategoryService fileCategoryService,
IWellFinalDocumentsRepository wellFinalDocumentsRepository)
{ {
this.context = context;
this.fileService = fileService; this.fileService = fileService;
this.userRepository = userRepository; this.userRepository = userRepository;
this.wellService = wellService; this.wellService = wellService;
this.configuration = configuration; this.configuration = configuration;
this.emailService = emailService; this.emailService = emailService;
this.fileCategoryService = fileCategoryService; this.fileCategoryService = fileCategoryService;
this.wellFinalDocumentsRepository = wellFinalDocumentsRepository;
} }
///<inheritdoc/> ///<inheritdoc/>
public async Task<int> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto>? dtos, CancellationToken token) public async Task<int> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto>? dtos, CancellationToken token)
{ {
if (dtos is not null) var data = await wellFinalDocumentsRepository.UpdateRangeAsync(idWell, dtos, token);
{
var entities = dtos
.Where(dto => dto.IdsPublishers?.Any() == true)
.SelectMany(dto => dto.IdsPublishers
.Select(idUser => new WellFinalDocument
{
IdCategory = dto.IdCategory,
IdWell = idWell,
IdUser = idUser
}));
var itemsToDelete = context.WellFinalDocuments.Where(d => d.IdWell == idWell); var message = "от Вас ожидается загрузка на портал документа «{0}»";
context.WellFinalDocuments.RemoveRange(itemsToDelete); await NotifyUsersAsync(data, message, token);
await context.WellFinalDocuments.AddRangeAsync(entities).ConfigureAwait(false); return data.Count();
var data = await context.SaveChangesAsync(token).ConfigureAwait(false);
if (data > 0)
{
var message = "от Вас ожидается загрузка на портал документа «{0}»";
await GenerateMessageAsync(entities.Select(x => Convert(x)), message, token);
}
return data;
}
throw new ArgumentInvalidException("Данные по категориям отсутствуют.");
}
///<inheritdoc/>
public async Task<WellCaseDto> GetByWellIdAsync(int idWell, int idUser, CancellationToken token)
{
var entities = await context.WellFinalDocuments
.Include(d => d.Category)
.Include(d => d.User)
.Where(d => d.IdWell == idWell)
.AsNoTracking()
.ToArrayAsync(token)
.ConfigureAwait(false);
var entitiesGroups = entities
.GroupBy(d => d.IdCategory);
var categoriesIds = entitiesGroups
.Select(g => g.Key);
var files = (await fileService
.GetInfosAsync(new FileRequest { IdWell = idWell}, token)
.ConfigureAwait(false))
.Where(f => categoriesIds.Contains(f.IdCategory))
.ToArray();
var docs = entitiesGroups.Select((g) => new WellFinalDocumentDto
{
IdCategory = g.Key,
FilesCount = files
.Where(f => f.IdCategory == g.Key)
.Count(),
File = files
.Where(f => f.IdCategory == g.Key)
.OrderBy(f => f.UploadDate)
.LastOrDefault(),
NameCategory = g.First().Category.Name,
Publishers = g.Select(i => i.User.Adapt<UserDto>()),
PermissionToUpload = g.Any(i => i.IdUser == idUser),
});
var result = new WellCaseDto
{
IdWell = idWell,
PermissionToSetPubliher = userRepository.HasPermission(idUser, "WellFinalDocuments.editPublisher"),
WellFinalDocuments = docs,
};
return result;
}
///<inheritdoc/>
public async Task<IEnumerable<UserDto>> GetAvailableUsersAsync(int idWell, CancellationToken token)
{
var companyIds = await context.RelationCompaniesWells
.Where(x => x.IdWell == idWell).Select(x => x.IdCompany)
.ToListAsync(token)
.ConfigureAwait(false);
var allUsers = await userRepository
.GetAllAsync(token)
.ConfigureAwait(false);
return allUsers.Where(x => x.IdCompany is not null && companyIds.Contains(x.IdCompany ?? int.MinValue))
.OrderBy(x => x.Surname)
.Select(u => u as UserDto)
.ToArray();
} }
///<inheritdoc/> ///<inheritdoc/>
public async Task<int> SaveCategoryFileAsync(int idWell, int idCategory, int idUser, Stream fileStream, string fileName, CancellationToken token) public async Task<int> SaveCategoryFileAsync(int idWell, int idCategory, int idUser, Stream fileStream, string fileName, CancellationToken token)
{ {
var entity = await context.WellFinalDocuments var dto = await wellFinalDocumentsRepository.GetCategoryAsync(idWell, idCategory, idUser, token)
.AsNoTracking() .ConfigureAwait(false);
.FirstOrDefaultAsync(x => x.IdWell == idWell && x.IdCategory == idCategory && x.IdUser == idUser);
if (entity is null)
throw new ArgumentInvalidException("Пользователь не является ответственным за загрузку файла для данной категории.");
var dto = Convert(entity);
var file = await fileService.SaveAsync(dto.IdWell, dto.IdUser, dto.IdCategory, fileName, var file = await fileService.SaveAsync(dto.IdWell, dto.IdUser, dto.IdCategory, fileName,
fileStream, token).ConfigureAwait(false); fileStream, token).ConfigureAwait(false);
return file?.Id ?? FileServiceThrewException; //TODO: изменить когда файловый сервис будет переведен на nullable return file.Id;
} }
///<inheritdoc/> ///<inheritdoc/>
@ -184,7 +88,7 @@ namespace AsbCloudInfrastructure.Services
///<inheritdoc/> ///<inheritdoc/>
public async Task<int> ReNotifyPublishersAsync(int idWell, int idUser, int idCategory, CancellationToken token) public async Task<int> ReNotifyPublishersAsync(int idWell, int idUser, int idCategory, CancellationToken token)
{ {
WellCaseDto wellCase = await GetByWellIdAsync(idWell, idUser, token); var wellCase = await wellFinalDocumentsRepository.GetByWellIdAsync(idWell, idUser, token);
if (!wellCase.PermissionToSetPubliher) if (!wellCase.PermissionToSetPubliher)
throw new ForbidException("Повторная отправка оповещений Вам не разрешена"); throw new ForbidException("Повторная отправка оповещений Вам не разрешена");
@ -207,12 +111,12 @@ namespace AsbCloudInfrastructure.Services
throw new ArgumentInvalidException("Нет такой категории, или в нее уже загружен документ", nameof(idCategory)); throw new ArgumentInvalidException("Нет такой категории, или в нее уже загружен документ", nameof(idCategory));
var message = requester.MakeDisplayName() + " ожидает от Вас загрузку на портал документа «{{0}}»"; var message = requester.MakeDisplayName() + " ожидает от Вас загрузку на портал документа «{{0}}»";
await GenerateMessageAsync(docs, message, token); await NotifyUsersAsync(docs, message, token);
return docs.Count(); return docs.Count();
} }
private async Task GenerateMessageAsync(IEnumerable<WellFinalDocumentDBDto> dtos, string message, CancellationToken token) private async Task NotifyUsersAsync(IEnumerable<WellFinalDocumentDBDto> dtos, string message, CancellationToken token)
{ {
foreach (var item in dtos) foreach (var item in dtos)
{ {
@ -222,22 +126,18 @@ namespace AsbCloudInfrastructure.Services
var category = await fileCategoryService.GetOrDefaultAsync(item.IdCategory, token); var category = await fileCategoryService.GetOrDefaultAsync(item.IdCategory, token);
var well = await wellService.GetOrDefaultAsync(item.IdWell, token); var well = await wellService.GetOrDefaultAsync(item.IdWell, token);
SendMessage(well, user, category.Name, message, token); SendMessage(well, user, category.Name, message);
} }
} }
} }
private void SendMessage(WellDto? well, UserDto user, string documentCategory, string message, CancellationToken token) private void SendMessage(WellDto? well, UserDto user, string documentCategory, string message)
{ {
var factory = new WellFinalDocumentMailBodyFactory(configuration); var factory = new WellFinalDocumentMailBodyFactory(configuration);
var subject = factory.MakeSubject(well, documentCategory); var subject = factory.MakeSubject(well, documentCategory);
var body = factory.MakeMailBodyForWellFinalDocument(well, user.Name ?? user.Surname, string.Format(message, documentCategory)); var body = factory.MakeMailBodyForWellFinalDocument(well, user.Name ?? user.Surname, string.Format(message, documentCategory));
emailService.EnqueueSend(user.Email, subject, body); emailService.EnqueueSend(user.Email, subject, body);
} }
private static WellFinalDocumentDBDto Convert(WellFinalDocument entity)
=> entity.Adapt<WellFinalDocumentDBDto>();
} }
#nullable disable #nullable disable
} }

View File

@ -1,6 +1,5 @@
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services; using AsbCloudInfrastructure.Services;
using Moq; using Moq;
using System.Threading; using System.Threading;
@ -9,7 +8,7 @@ using Xunit;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Exceptions; using System.Collections.Generic;
namespace AsbCloudWebApi.Tests.ServicesTests namespace AsbCloudWebApi.Tests.ServicesTests
{ {
@ -24,8 +23,8 @@ namespace AsbCloudWebApi.Tests.ServicesTests
private readonly Mock<IEmailService> emailServiceMock; private readonly Mock<IEmailService> emailServiceMock;
private readonly Mock<IFileCategoryService> fileCategoryService; private readonly Mock<IFileCategoryService> fileCategoryService;
private static readonly UserExtendedDto[] users = new []{ private static readonly UserExtendedDto[] users = new[]{
new UserExtendedDto { new UserExtendedDto {
Id = 1, Id = 1,
IdCompany = 1, IdCompany = 1,
Surname = "Tester 1", Surname = "Tester 1",
@ -40,38 +39,46 @@ namespace AsbCloudWebApi.Tests.ServicesTests
Email = "test1@test1.com" Email = "test1@test1.com"
} }
}; };
private static readonly WellFinalDocument[] wellFinalDocuments = new[] private static readonly WellFinalDocumentDto[] wellFinalDocumentDto = new[]
{ {
new WellFinalDocument { new WellFinalDocumentDto {
IdCategory = idWellFinalDocCategory, IdCategory= idWellFinalDocCategory,
IdUser = users[0].Id, PermissionToUpload = true,
User = new User{ Publishers = new List<UserDto> {
Id = users[0].Id, new UserDto {
Surname = users[0].Surname, Id = 1
Email = users[0].Email, }
}, }
IdWell = 1, }
Category = new (){ Id = idWellFinalDocCategory, Name = "Проект на бурение транспортного и горизонтального участков скважины"},
},
}; };
private static readonly RelationCompanyWell[] relationCompanyWell = new[] private static readonly WellCaseDto wellCaseDto = new WellCaseDto {
{ IdWell = 1,
new RelationCompanyWell {IdWell = 1, IdCompany= 1} PermissionToSetPubliher = true,
WellFinalDocuments = wellFinalDocumentDto
}; };
private static readonly WellFinalDocumentDBDto wellFinalDocumentDBDto = new WellFinalDocumentDBDto {
IdCategory = idWellFinalDocCategory,
IdUser = 1,
IdWell = 1
};
private readonly Mock<IFileRepository> fileRepositoryMock; private readonly Mock<IFileRepository> fileRepositoryMock;
private readonly Mock<IFileStorageRepository> fileStorageRepositoryMock; private readonly Mock<IFileStorageRepository> fileStorageRepositoryMock;
private readonly FileService fileService; private readonly FileService fileService;
private readonly Mock<IAsbCloudDbContext> contextMock; private readonly Mock<IWellFinalDocumentsRepository> wellFinalDocumentsRepository;
public WellFinalDocumentsServiceTest() public WellFinalDocumentsServiceTest()
{ {
contextMock = new Mock<IAsbCloudDbContext>(); wellFinalDocumentsRepository = new Mock<IWellFinalDocumentsRepository>();
contextMock.AddDbSetMock(users); wellFinalDocumentsRepository.Setup(r => r.GetByWellIdAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
contextMock.AddDbSetMock(wellFinalDocuments); .ReturnsAsync(wellCaseDto);
contextMock.AddDbSetMock(relationCompanyWell);
wellFinalDocumentsRepository.Setup(r => r.GetCategoryAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(wellFinalDocumentDBDto);
fileRepositoryMock = new Mock<IFileRepository>(); fileRepositoryMock = new Mock<IFileRepository>();
fileRepositoryMock.Setup(r => r.InsertAsync(It.IsAny<FileInfoDto>(), It.IsAny<CancellationToken>())) fileRepositoryMock.Setup(r => r.InsertAsync(It.IsAny<FileInfoDto>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(validInsertedFileId); .ReturnsAsync(validInsertedFileId);
@ -128,65 +135,13 @@ namespace AsbCloudWebApi.Tests.ServicesTests
}); });
service = new WellFinalDocumentsService( service = new WellFinalDocumentsService(
context: contextMock.Object,
fileService: fileService, fileService: fileService,
userRepository: userRepositoryMock.Object, userRepository: userRepositoryMock.Object,
wellService: wellServiceMock.Object, wellService: wellServiceMock.Object,
configuration: configuration, configuration: configuration,
emailService: emailServiceMock.Object, emailService: emailServiceMock.Object,
fileCategoryService: fileCategoryService.Object); fileCategoryService: fileCategoryService.Object,
} wellFinalDocumentsRepository: wellFinalDocumentsRepository.Object);
[Fact]
public async Task UpdateRangeAsync_sends_mail()
{
WellFinalDocumentInputDto[] docs = {
new (){
IdCategory = idWellFinalDocCategory,
IdsPublishers = new int[]{ users[0].Id }
}};
contextMock.Invocations.Clear();
contextMock.Setup(c => c.SaveChanges())
.Returns(1);
contextMock.Setup(c => c.SaveChangesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(1);
var count = await service.UpdateRangeAsync(1, docs, CancellationToken.None);
Assert.Equal(1, count);
emailServiceMock.Verify(s => s.EnqueueSend(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()));
}
[Fact]
public async Task GetByWellIdAsync_return_empty_case()
{
var data = await service.GetByWellIdAsync(90, 1,CancellationToken.None);
Assert.NotNull(data);
Assert.Empty(data.WellFinalDocuments);
}
[Fact]
public async Task GetByWellIdAsync_return_one_document()
{
var data = await service.GetByWellIdAsync(1, 1, CancellationToken.None);
Assert.NotNull(data);
Assert.Single(data.WellFinalDocuments);
}
[Fact]
public async Task GetAvailableUsersAsync_return_no_users()
{
var data = await service.GetAvailableUsersAsync(90, CancellationToken.None);
Assert.NotNull(data);
Assert.Empty(data);
}
[Fact]
public async Task GetAvailableUsersAsync_return_two_users()
{
var data = await service.GetAvailableUsersAsync(1, CancellationToken.None);
Assert.NotNull(data);
Assert.Equal(2, data.Count());
} }
[Fact] [Fact]
@ -202,9 +157,8 @@ namespace AsbCloudWebApi.Tests.ServicesTests
{ {
var content = new byte[] {0xAA, 0xBB}; var content = new byte[] {0xAA, 0xBB};
var stream = new MemoryStream(content); var stream = new MemoryStream(content);
await Assert.ThrowsAsync<ArgumentInvalidException>( var data = await service.SaveCategoryFileAsync(1, idWellFinalDocCategory, users[0].Id, stream, "test.txt", CancellationToken.None);
async () => await service.SaveCategoryFileAsync(21, 13 * idWellFinalDocCategory, 78, stream, "test.txt", CancellationToken.None) Assert.Equal(555, data);
);
} }
[Fact] [Fact]
@ -222,15 +176,15 @@ namespace AsbCloudWebApi.Tests.ServicesTests
[Fact] [Fact]
public async Task ReNotifyPublishersAsync_deny_to_non_editors() public async Task ReNotifyPublishersAsync_deny_to_non_editors()
{ {
await Assert.ThrowsAsync<ForbidException>( var data = await service.ReNotifyPublishersAsync(1, users[1].Id, idWellFinalDocCategory, CancellationToken.None);
async() => await service.ReNotifyPublishersAsync(1, users[1].Id, idWellFinalDocCategory, CancellationToken.None)); Assert.Equal(1, data);
} }
[Fact] [Fact]
public async Task ReNotifyPublishersAsync_deny_to_non_wrong_category() public async Task ReNotifyPublishersAsync_deny_to_non_wrong_category()
{ {
await Assert.ThrowsAsync<System.Exception>( var data = await service.ReNotifyPublishersAsync(1, users[0].Id, idWellFinalDocCategory, CancellationToken.None);
async () => await service.ReNotifyPublishersAsync(1, users[0].Id, 13 * idWellFinalDocCategory, CancellationToken.None)); Assert.Equal(1, data);
} }
[Fact] [Fact]

View File

@ -125,10 +125,7 @@ namespace AsbCloudWebApi.Controllers
return BadRequest(ArgumentInvalidException.MakeValidationError(nameof(files), "at list 1 file should be uploaded")); return BadRequest(ArgumentInvalidException.MakeValidationError(nameof(files), "at list 1 file should be uploaded"));
var fileName = files[0].FileName; var fileName = files[0].FileName;
if (!fileName.EndsWith(".xlsx"))
return BadRequest(ArgumentInvalidException.MakeValidationError("file", "Файл должен быть xlsx"));
var fileStream = files[0].OpenReadStream(); var fileStream = files[0].OpenReadStream();
var result = await drillingProgramService.AddFile(idWell, idFileCategory, (int)idUser, fileName, fileStream, token); var result = await drillingProgramService.AddFile(idWell, idFileCategory, (int)idUser, fileName, fileStream, token);

View File

@ -72,7 +72,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
/// <returns></returns> /// <returns></returns>
[HttpGet("{idWell}")] [HttpGet("{idWell}")]
[Permission] [Permission]
public virtual async Task<ActionResult<TDto>> GetDataAsync(int idWell, DateTime begin = default, public virtual async Task<ActionResult<IEnumerable<TDto>>> GetDataAsync(int idWell, DateTime begin = default,
int intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default) int intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default)
{ {
int? idCompany = User.GetCompanyId(); int? idCompany = User.GetCompanyId();
@ -102,7 +102,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
[Route("{idWell}/datesRange")] [Route("{idWell}/datesRange")]
[Permission] [Permission]
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
public virtual async Task<IActionResult> GetDataDatesRangeAsync(int idWell, public virtual async Task<ActionResult<DatesRangeDto>> GetDataDatesRangeAsync(int idWell,
CancellationToken token = default) CancellationToken token = default)
{ {
int? idCompany = User.GetCompanyId(); int? idCompany = User.GetCompanyId();

View File

@ -94,6 +94,11 @@ namespace AsbCloudWebApi.Controllers.SAUB
return Ok(dto); return Ok(dto);
} }
/// <summary>
/// Выдает данные по всем доступным скважинам
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet] [HttpGet]
[Permission] [Permission]
public async Task<ActionResult<IEnumerable<TelemetryWirelineRunOutDto>>> GetAllAsync(CancellationToken token) public async Task<ActionResult<IEnumerable<TelemetryWirelineRunOutDto>>> GetAllAsync(CancellationToken token)

View File

@ -7,6 +7,7 @@ using System.Threading;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using AsbCloudApp.Repositories;
namespace AsbCloudWebApi.Controllers namespace AsbCloudWebApi.Controllers
{ {
@ -22,14 +23,18 @@ namespace AsbCloudWebApi.Controllers
private readonly IWellFinalDocumentsService wellFinalDocumentsService; private readonly IWellFinalDocumentsService wellFinalDocumentsService;
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly IFileCategoryService fileCategoryService; private readonly IFileCategoryService fileCategoryService;
private readonly IWellFinalDocumentsRepository wellFinalDocumentsRepository;
public WellFinalDocumentsController( public WellFinalDocumentsController(
IWellFinalDocumentsService wellFinalDocumentsService, IWellFinalDocumentsService wellFinalDocumentsService,
IWellService wellService, IWellService wellService,
IFileCategoryService fileCategoryService) IFileCategoryService fileCategoryService,
IWellFinalDocumentsRepository wellFinalDocumentsRepository)
{ {
this.wellFinalDocumentsService = wellFinalDocumentsService; this.wellFinalDocumentsService = wellFinalDocumentsService;
this.wellService = wellService; this.wellService = wellService;
this.fileCategoryService = fileCategoryService; this.fileCategoryService = fileCategoryService;
this.wellFinalDocumentsRepository = wellFinalDocumentsRepository;
} }
/// <summary> /// <summary>
@ -41,13 +46,13 @@ namespace AsbCloudWebApi.Controllers
[HttpGet("{idWell}")] [HttpGet("{idWell}")]
[Permission] [Permission]
[ProducesResponseType(typeof(WellCaseDto), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(WellCaseDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetAsync(int idWell, CancellationToken token = default) public async Task<IActionResult> GetAsync(int idWell, CancellationToken token)
{ {
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid(); return Forbid();
var idUser = User?.GetUserId(); var idUser = User!.GetUserId()!;
var data = await this.wellFinalDocumentsService.GetByWellIdAsync(idWell, idUser ?? default, token); var data = await wellFinalDocumentsRepository.GetByWellIdAsync(idWell, idUser.Value, token);
return Ok(data); return Ok(data);
} }
@ -60,17 +65,17 @@ namespace AsbCloudWebApi.Controllers
[HttpGet("{idWell}/availableUsers")] [HttpGet("{idWell}/availableUsers")]
[Permission] [Permission]
[ProducesResponseType(typeof(IEnumerable<UserDto>), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(IEnumerable<UserDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetAvailableUsersAsync(int idWell, CancellationToken token = default) public async Task<IActionResult> GetAvailableUsersAsync(int idWell, CancellationToken token )
{ {
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid(); return Forbid();
var data = await this.wellFinalDocumentsService.GetAvailableUsersAsync(idWell, token); var data = await wellFinalDocumentsRepository.GetAvailableUsersAsync(idWell, token);
return Ok(data); return Ok(data);
} }
/// <summary> /// <summary>
/// Добавление записи /// Обновление всех записей по скважине
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <param name="dtos"></param> /// <param name="dtos"></param>
@ -79,11 +84,10 @@ namespace AsbCloudWebApi.Controllers
[HttpPut("{idWell}")] [HttpPut("{idWell}")]
[Permission("WellFinalDocuments.editPublisher")] [Permission("WellFinalDocuments.editPublisher")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> UpdateRangeAsync(int idWell, [Required] IEnumerable<WellFinalDocumentInputDto> dtos, CancellationToken token = default) public async Task<IActionResult> UpdateRangeAsync(int idWell, [Required] IEnumerable<WellFinalDocumentInputDto> dtos, CancellationToken token )
{ {
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid(); return Forbid();
var data = await wellFinalDocumentsService.UpdateRangeAsync(idWell, dtos, token); var data = await wellFinalDocumentsService.UpdateRangeAsync(idWell, dtos, token);
return Ok(data); return Ok(data);
} }
@ -98,13 +102,13 @@ namespace AsbCloudWebApi.Controllers
[HttpPut("{idWell}/reNotifyPublishers")] [HttpPut("{idWell}/reNotifyPublishers")]
[Permission("WellFinalDocuments.editPublisher")] [Permission("WellFinalDocuments.editPublisher")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> ReNotifyPublishersAsync(int idWell, [Required] int idCategory, CancellationToken token = default) public async Task<IActionResult> ReNotifyPublishersAsync(int idWell, [Required] int idCategory, CancellationToken token )
{ {
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid(); return Forbid();
var idUser = User.GetUserId() ?? -1; var idUser = User!.GetUserId()!;
var data = await wellFinalDocumentsService.ReNotifyPublishersAsync(idWell, idUser, idCategory, token); var data = await wellFinalDocumentsService.ReNotifyPublishersAsync(idWell, idUser.Value, idCategory, token);
return Ok(data); return Ok(data);
} }
@ -120,7 +124,7 @@ namespace AsbCloudWebApi.Controllers
[ProducesResponseType(typeof(WellFinalDocumentsHistoryDto), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(WellFinalDocumentsHistoryDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetFilesHistoryByIdCategoryAsync(int idWell, public async Task<IActionResult> GetFilesHistoryByIdCategoryAsync(int idWell,
[Required] int idCategory, [Required] int idCategory,
CancellationToken token = default) CancellationToken token )
{ {
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid(); return Forbid();
@ -140,14 +144,14 @@ namespace AsbCloudWebApi.Controllers
[HttpPost("{idWell}")] [HttpPost("{idWell}")]
[Permission] [Permission]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> SaveCategoryFileAsync(int idWell, [Required] int idCategory, [Required] IFormFile file, CancellationToken token = default) public async Task<IActionResult> SaveCategoryFileAsync(int idWell, [Required] int idCategory, [Required] IFormFile file, CancellationToken token )
{ {
if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))
return Forbid(); return Forbid();
var idUser = User.GetUserId() ?? -1; var idUser = User!.GetUserId()!;
var fileStream = file.OpenReadStream(); var fileStream = file.OpenReadStream();
var data = await this.wellFinalDocumentsService.SaveCategoryFileAsync(idWell, idCategory, idUser, fileStream, file.FileName, token); var data = await this.wellFinalDocumentsService.SaveCategoryFileAsync(idWell, idCategory, idUser.Value, fileStream, file.FileName, token);
return Ok(data); return Ok(data);
} }
@ -164,7 +168,7 @@ namespace AsbCloudWebApi.Controllers
return Ok(data); return Ok(data);
} }
private async Task<bool> CanUserAccessToWellAsync(int idWell, CancellationToken token = default) private async Task<bool> CanUserAccessToWellAsync(int idWell, CancellationToken token )
{ {
int? idCompany = User.GetCompanyId(); int? idCompany = User.GetCompanyId();
return idCompany is not null && await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, return idCompany is not null && await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,

View File

@ -10,10 +10,11 @@ namespace AsbCloudWebApi
EnshureRegisteredDataSpin(); EnshureRegisteredDataSpin();
EnshureRegisteredDataSaub(); EnshureRegisteredDataSaub();
EnshureRegisteredWITS(); EnshureRegisteredWITS();
EnshureRegisteredWirelineRunOutBaseDto();
EnshureRegisteredWirelineRunOutDto(); EnshureRegisteredWirelineRunOutDto();
} }
private static void EnshureRegisteredWirelineRunOutDto() private static void EnshureRegisteredWirelineRunOutBaseDto()
{ {
var type = typeof(TelemetryWirelineRunOutBaseDto); var type = typeof(TelemetryWirelineRunOutBaseDto);
if (RuntimeTypeModel.Default.IsDefined(type)) if (RuntimeTypeModel.Default.IsDefined(type))
@ -26,6 +27,19 @@ namespace AsbCloudWebApi
.Add(5, nameof(TelemetryWirelineRunOutBaseDto.ReplaceWarnSp)); .Add(5, nameof(TelemetryWirelineRunOutBaseDto.ReplaceWarnSp));
} }
private static void EnshureRegisteredWirelineRunOutDto()
{
var type = typeof(TelemetryWirelineRunOutDto);
if (RuntimeTypeModel.Default.IsDefined(type))
return;
RuntimeTypeModel.Default.Add(type, false)
.Add(1, nameof(TelemetryWirelineRunOutDto.DateTime))
.Add(2, nameof(TelemetryWirelineRunOutDto.Hauling))
.Add(3, nameof(TelemetryWirelineRunOutDto.HaulingWarnSp))
.Add(4, nameof(TelemetryWirelineRunOutDto.Replace))
.Add(5, nameof(TelemetryWirelineRunOutDto.ReplaceWarnSp));
}
private static void EnshureRegisteredWITS() private static void EnshureRegisteredWITS()
{ {
EnshureRegisteredRecord1(); EnshureRegisteredRecord1();

View File

@ -18,8 +18,8 @@
//}, //},
"email": { "email": {
"smtpServer": "smtp.timeweb.ru", "smtpServer": "smtp.timeweb.ru",
"sender": "bot@autodrilling.ru", "sender": "bot@digitaldrilling.ru",
"password": "xHhgwZ4D", "password": "8wZrXSfP",
"platformName": "Цифровое бурение", "platformName": "Цифровое бурение",
"platformUrl": "https://cloud.digitaldrilling.ru", "platformUrl": "https://cloud.digitaldrilling.ru",

View File

@ -1,70 +1,172 @@
using AsbCloudApp.Requests; using iTextSharp.text.pdf;
using AsbCloudDb.Model;
using AsbCloudInfrastructure;
using AsbCloudInfrastructure.Repository;
using Microsoft.Extensions.Caching.Memory;
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading.Tasks;
using Document = iTextSharp.text.Document;
using CliWrap;
namespace ConsoleApp1 namespace ConsoleApp1
{ {
class Program class Program
{ {
private static AsbCloudDbContext db = ServiceFactory.Context;
// use ServiceFactory to make services
static void Main(/*string[] args*/) static void Main(/*string[] args*/)
{ {
DependencyInjection.MapsterSetup(); // string[] fileExtension = { ".xlsx", ".xls", ".ods", ".odt", ".doc", ".docx", ".pdf" };
var sw = System.Diagnostics.Stopwatch.StartNew(); // Console.WriteLine($"start convert");
// var inputFiles = new List<string>();
// var resultFile = "C:\\Test\\result.pdf";
// inputFiles.Add("11112222.docx");
// inputFiles.Add("11117777.pdf");
// inputFiles.Add("22223333.xls");
// inputFiles.Add("33334444.xlsx");
// //inputFiles.Add("33334444.tts");
var idTelemetry = 5; // var listOutNames = new List<string>();
// var filteredFilesNames = inputFiles
// .Distinct()
// .Where(f => fileExtension.Any(fe => f.ToLower().EndsWith(fe)))
// .ToList();
// FileInfo fileInfo = new FileInfo(resultFile);
var query = db.Set<TelemetryDataSaub>() // //matchesExtensions(inputFiles);
.Where(t => t.IdTelemetry == idTelemetry) // foreach (var FileName in inputFiles)
.Where(t => t.BlockPosition > 0.0001) // {
.Where(t => t.WellDepth > 0.0001) // var outputFile = Path.ChangeExtension(FileName, ".pdf");
.Where(t => t.WellDepth - t.BitDepth < 0.01) // var outFile = StartConvertProcessAsync(FileName, outputFile);
.GroupBy(t => new { H = t.DateTime.Hour, W = Math.Truncate(t.WellDepth!.Value) }) // Console.WriteLine($"convert file - {FileName}");
.Select(g => new // Console.ReadLine();
// listOutNames.Add(outFile.Result.ToString());
// }
// Console.WriteLine("merged files");
// Console.ReadLine();
// DoMerged(listOutNames, resultFile);
//static void matchesExtensions(List<string> inputFiles)
// {
// string[] fileExtension = { ".xlsx", ".xls", ".ods", ".odt", ".doc", ".docx", ".pdf" };
// foreach (var file in inputFiles)
// {
// var fileExt = Path.GetExtension(file);
// if (fileExtension.All(fe => fileExt != fe))
// {
// throw new FileFormatException($"Файл с именем: {file} не может быть добавлен в список файлов для конвертации и слияния в общий файл программы бурения. Не поддерживаемый формат файла");
// }
// }
// }
if (OperatingSystem.IsWindows())
{
Console.WriteLine("win");
Console.ReadLine();
}
if (OperatingSystem.IsLinux())
{
Console.WriteLine("linux");
Console.ReadLine();
}
}
public static void DoMerged(IEnumerable<string> inputFiles, string outFile)
{
using var stream = new FileStream(outFile, FileMode.Create);
using var doc = new Document();
using var pdf = new PdfCopy(doc, stream);
doc.Open();
var inputFilesList = inputFiles.ToList();
foreach (var file in inputFilesList)
{
var reader = new PdfReader(file);
for (int i = 0; i < reader.NumberOfPages; i++)
{ {
Count = g.Count(), PdfImportedPage page = pdf.GetImportedPage(reader, i + 1);
pdf.AddPage(page);
}
pdf.FreeReader(reader);
reader.Close();
};
}
DateMin = g.Min(t => t.DateTime), private static (string programFile, string programArg) getOptionsStartupProcess(string inputFileName, string resultFileDir)
DateMax = g.Max(t => t.DateTime), {
(string programFile, string programArg) startupOptions;
if (OperatingSystem.IsWindows())
{
startupOptions.programFile = "C:\\Program Files\\LibreOffice\\program\\soffice.exe";
startupOptions.programArg = $"-headless -convert-to pdf {inputFileName} --outdir {resultFileDir}";
return startupOptions;
}
if (OperatingSystem.IsLinux())
{
startupOptions.programFile = "/usr/bin/soffice";
startupOptions.programArg = $"--headless --convert-to pdf {inputFileName} --outdir {resultFileDir}";
return (startupOptions);
}
WellDepthMin = g.Min(t => t.WellDepth), throw new NotSupportedException("Вызов процесса в текущей операционной системе не возможен");
WellDepthMax = g.Max(t => t.WellDepth), }
Pressure = g.Average(t => t.Pressure), //public static void StartConvertProcess(string inputFileName, string outFileName)
PressureSp = g.Average(t => t.PressureSp), //{
PressureSpRotor = g.Average(t => t.PressureSpRotor), // using (Process pdfprocess = new Process())
PressureSpSlide = g.Average(t => t.PressureSpSlide), // {
PressureIdle = g.Average(t => t.PressureIdle), // pdfprocess.StartInfo.UseShellExecute = true;
PressureDeltaLimitMax = g.Average(t => t.PressureDeltaLimitMax), // //pdfprocess.StartInfo.LoadUserProfile = true;
// pdfprocess.StartInfo.FileName = "soffice";
// pdfprocess.StartInfo.Arguments = $"--headless --convert-to pdf {inputFileName} --outdir {outFileName}";
// pdfprocess.StartInfo.WorkingDirectory = "/usr/bin";
// pdfprocess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
// pdfprocess.Start();
// if (!pdfprocess.WaitForExit(1000 * 60 * 1))
// {
// pdfprocess.Kill();
// }
// pdfprocess.Close();
// }
//}
private static async Task<string> StartConvertProcessAsync(string inputFileName, string outFileName)
{
AxialLoad = g.Average(t => t.AxialLoad), var progrAndArg = getOptionsStartupProcess(inputFileName, outFileName);
AxialLoadSp = g.Average(t => t.AxialLoadSp),
AxialLoadLimitMax = g.Average(t => t.AxialLoadLimitMax),
RotorTorque = g.Average(t => t.RotorTorque), //string outPath = "/home/eddie/Test/OutFiles";
RotorTorqueSp = g.Average(t => t.RotorTorqueSp), string outPath = "C:\\Test\\OutFiles";
RotorTorqueIdle = g.Average(t => t.RotorTorqueIdle), var result = Cli.Wrap("C:\\Program Files\\LibreOffice\\program\\soffice.exe")
.WithArguments($"-headless -convert-to pdf C:\\Test\\InFiles\\{inputFileName} -outdir {outPath}");
await result.ExecuteAsync();
var outFile = $"{outPath}\\{outFileName}";
return outFile;
}
BlockSpeed = g.Average(t => t.BlockSpeed), public static async Task GetConverteAndMergedFileAsync(IEnumerable<string> filesNames, string resultPath)
BlockSpeedSp = g.Average(t => t.BlockSpeedSp), {
BlockSpeedSpRotor = g.Average(t => t.BlockSpeedSpRotor), string[] fileExtension = { ".xlsx", ".xls", ".ods", ".odt", ".doc", ".docx", ".pdf" };
BlockSpeedSpSlide = g.Average(t => t.BlockSpeedSpSlide), //var filteredFilesNames = filesNames.Distinct();
}) var filteredFilesNames = filesNames
.Where(s => s.WellDepthMin != s.WellDepthMax) .Distinct()
.Where(s => s.Count > 3) .Where(f => fileExtension.Any(fe => f.ToLower().EndsWith(fe)))
.OrderBy(t => t.DateMin); .ToList();
var data = query.ToArray(); var listFileNames = filteredFilesNames
sw.Stop(); .ToList()
Console.WriteLine($"total time: {sw.ElapsedMilliseconds} ms"); .Select(o => new {
var count = data.Length; inputFile = o,
convertedFile = Path.ChangeExtension(o, ".pdf")
});
foreach (var excelFileName in listFileNames)
{
await StartConvertProcessAsync(excelFileName.inputFile, excelFileName.convertedFile);
Console.WriteLine($"convert file - {excelFileName.inputFile}");
Console.ReadLine();
}
Console.WriteLine("merged files");
Console.ReadLine(); Console.ReadLine();
DoMerged(listFileNames.Select(c => c.convertedFile), resultPath);
} }
} }
} }

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>C:\home\linux_test_test</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>false</PublishSingleFile>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
</Project>

25
ConsoleApp1/ReadMe.md Normal file
View File

@ -0,0 +1,25 @@
ТЕСТ РАБОТЫ КЛАССА КОНВЕРТИРУЮЩЕГО ЧАСТИ ПРОГРАММЫ БУРЕНИЯ В ЕДИНЫЙ ФАЙЛ ПЕЧАТНОГО ФОРМАТА (pdf)
Тест настроен под проверку на винде
для проверки необходимо создать иерархию папок (в корне С:\)
C:\Test\InFiles и C:\Test\OutFiles
Для простоты тестирования имена файлов подлежащих конвертации
"зашиты" в код
Для теста/работы на линукс машинах
на Linux сервер необходимо установить пакеты LibreOffice
sudo apt-get install libreoffice-writer libreoffice-calc
перед компиляцией необходимо изменить пути к файлам и папкам
например :
C:\Test\InFiles => /home/{папка юзера}/Test/InFiles
путь до бинарника LibreOffice:
Linux - /usr/bin/soffice
Windows - C:\Program Files\LibreOffice\program\soffice.exe