diff --git a/AsbCloudApp/Data/User/ContactDto.cs b/AsbCloudApp/Data/User/ContactDto.cs index adf4793a..904d4ca2 100644 --- a/AsbCloudApp/Data/User/ContactDto.cs +++ b/AsbCloudApp/Data/User/ContactDto.cs @@ -1,61 +1,60 @@ using System; using System.ComponentModel.DataAnnotations; -namespace AsbCloudApp.Data.User +namespace AsbCloudApp.Data.User; + +/// +/// Контакт +/// +public class ContactDto : IId { + /// + public int Id { get; set; } + /// - /// Контакт + /// ключ типа компании /// + [Required] + [Range(1, int.MaxValue)] + public int IdCompanyType { get; set; } - public class ContactDto : IId - { - /// - public int Id { get; set; } + /// + /// ключ скважины + /// + [Required] + [Range(1,int.MaxValue)] + public int IdWell { get; set; } - /// - /// ключ типа компании - /// - [Required] - public int IdCompanyType { get; set; } + /// + /// ФИО + /// + [Required] + [StringLength(260, MinimumLength = 0, ErrorMessage = "Допустимая длина ФИО от 1 до 260 символов")] + public string FullName { get; set; } = null!; - /// - /// ключ скважины - /// - [Required] - public int IdWell { get; set; } + /// + /// Email + /// + [RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "Некорректный email")] + public string? Email { get; set; } - /// - /// ФИО - /// - [Required] - [StringLength(260, MinimumLength = 0, ErrorMessage = "Допустимая длина ФИО от 1 до 260 символов")] - public string FullName { get; set; } = null!; + /// + /// Phone + /// + [RegularExpression(@"^(?:\+7|8)\s?(?:\(\d{3}\)|\d{3})\s?\d{3}-?\d{2}-?\d{2}$", ErrorMessage = "Некорректный номер телефона")] + public string? Phone { get; set; } - /// - /// Email - /// - [RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "Некорректный email")] - public string? Email { get; set; } + /// + /// Должность + /// + [Required] + [StringLength(260, MinimumLength = 1, ErrorMessage = "Допустимая длина должности от 1 до 260 символов")] + public string Position { get; set; } = null!; - /// - /// Phone - /// - [RegularExpression(@"^(?:\+7|8)\s?(?:\(\d{3}\)|\d{3})\s?\d{3}-?\d{2}-?\d{2}$", ErrorMessage = "Некорректный номер телефона")] - public string? Phone { get; set; } - - /// - /// Должность - /// - [Required] - [StringLength(260, MinimumLength = 1, ErrorMessage = "Допустимая длина должности от 1 до 260 символов")] - public string Position { get; set; } = null!; - - /// - /// Компания - /// - [Required] - [StringLength(260, MinimumLength = 3, ErrorMessage = "Допустимая длина должности от 3 до 260 символов")] - public string Company { get; set; } = null!; - - } + /// + /// Компания + /// + [Required] + [StringLength(260, MinimumLength = 3, ErrorMessage = "Допустимая длина названия компании от 3 до 260 символов")] + public string Company { get; set; } = null!; } diff --git a/AsbCloudDb/EFExtensionsInitialization.cs b/AsbCloudDb/EFExtensionsInitialization.cs index 7c265b82..7f9ba0ea 100644 --- a/AsbCloudDb/EFExtensionsInitialization.cs +++ b/AsbCloudDb/EFExtensionsInitialization.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using Microsoft.EntityFrameworkCore.Infrastructure; +using System.Diagnostics; namespace AsbCloudDb { @@ -12,12 +13,16 @@ namespace AsbCloudDb db.SetCommandTimeout(TimeSpan.FromMinutes(5)); if (db.EnsureCreated()) { + Trace.TraceInformation("Creating DB"); + Console.WriteLine("Creating DB"); db.CreateMigrationTable(); db.WriteMigrationsInfo(); } else { + Trace.TraceInformation("Migrating DB"); db.SetCommandTimeout(TimeSpan.FromMinutes(20)); + Console.WriteLine("db.Migrate()"); db.Migrate(); } } diff --git a/AsbCloudInfrastructure/Services/DailyReport/DailyReportTemplate.xlsx b/AsbCloudInfrastructure/Services/DailyReport/DailyReportTemplate.xlsx index 38d1bdba..5c2cbd12 100644 Binary files a/AsbCloudInfrastructure/Services/DailyReport/DailyReportTemplate.xlsx and b/AsbCloudInfrastructure/Services/DailyReport/DailyReportTemplate.xlsx differ diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs index b9c7d201..56aa2fd4 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs @@ -12,7 +12,6 @@ public class DetectorDrilling : DetectorAbstract public const string ExtraDataKeyHasOscillation = "hasOscillation"; public const string ExtraDataKeyDispersionOfNormalizedRotorSpeed = "dispersionOfNormalizedRotorSpeed"; public const string ExtraDataKeyAvgRotorSpeed = "avgRotorSpeed"; - public const string ExtraDataKeyIsAfbEnabled = "isAfbEnabled"; protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) { @@ -45,7 +44,8 @@ public class DetectorDrilling : DetectorAbstract => CalcRop(telemetry, begin, end); protected override bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) => - Math.Abs(operationDetectorResult.Operation.DepthStart - operationDetectorResult.Operation.DepthEnd) > 0.01; + base.IsValidOperationDetectorResult(operationDetectorResult) + && (operationDetectorResult.Operation.DepthEnd - operationDetectorResult.Operation.DepthStart) > 0.01; protected override (int Begin, int End) RefineEdges(DetectableTelemetry[] telemetry, int begin, int end) { diff --git a/AsbCloudInfrastructure/Startup.cs b/AsbCloudInfrastructure/Startup.cs index 4913b392..3af21638 100644 --- a/AsbCloudInfrastructure/Startup.cs +++ b/AsbCloudInfrastructure/Startup.cs @@ -38,13 +38,6 @@ namespace AsbCloudInfrastructure backgroundWorker.Add(MakeMemoryMonitoringWork(), TimeSpan.FromMinutes(1)); var notificationBackgroundWorker = provider.GetRequiredService(); - - Task.Delay(1_000) - .ContinueWith(async (_) => - { - await backgroundWorker.StartAsync(CancellationToken.None); - await notificationBackgroundWorker.StartAsync(CancellationToken.None); - }); } static Work MakeMemoryMonitoringWork() diff --git a/AsbCloudWebApi.Tests/Services/DetectedOperations/Detectors/DetectorDrillingTests.cs b/AsbCloudWebApi.Tests/Services/DetectedOperations/Detectors/DetectorDrillingTests.cs index 4dc797fb..8a61dd15 100644 --- a/AsbCloudWebApi.Tests/Services/DetectedOperations/Detectors/DetectorDrillingTests.cs +++ b/AsbCloudWebApi.Tests/Services/DetectedOperations/Detectors/DetectorDrillingTests.cs @@ -17,10 +17,10 @@ public class DetectorDrillingTests : DetectorDrilling public void DefineDrillingOperation_ShouldReturn_DrillingRotor(DetectableTelemetry[] telemetryRange) { //act - var result = GetIdOperation.Invoke(telemetryRange, 0, telemetryRange.Length); + var result = GetSpecificInformation(telemetryRange, 0, telemetryRange.Length); //assert - Assert.Equal(idRotor, result); + Assert.Equal(idRotor, result.IdCategory); } [Theory] @@ -28,10 +28,10 @@ public class DetectorDrillingTests : DetectorDrilling public void DefineDrillingOperation_ShouldReturn_DrillingSlide(DetectableTelemetry[] telemetryRange) { //act - var result = GetIdOperation.Invoke(telemetryRange, 0, telemetryRange.Length); + var result = GetSpecificInformation(telemetryRange, 0, telemetryRange.Length); //assert - Assert.Equal(idSlide, result); + Assert.Equal(idSlide, result.IdCategory); } [Theory] @@ -39,10 +39,13 @@ public class DetectorDrillingTests : DetectorDrilling public void DefineDrillingOperation_ShouldReturn_DrillingSlideWithOscillation(DetectableTelemetry[] telemetryRange) { //act - var result = GetIdOperation.Invoke(telemetryRange, 0, telemetryRange.Length); + var result = GetSpecificInformation(telemetryRange, 0, telemetryRange.Length); //assert - Assert.Equal(idSlideWithOscillation, result); + var oHasOscillation = result.ExtraData[ExtraDataKeyHasOscillation]; + + Assert.Equal(idSlide, result.IdCategory); + Assert.True(oHasOscillation is bool hasOscillation && hasOscillation); } [Fact] @@ -53,10 +56,11 @@ public class DetectorDrillingTests : DetectorDrilling { Operation = new DetectedOperation { - IdReasonOfEnd = IdReasonOfEnd_PressureIsLo, DepthStart = 5000, - DepthEnd = 6000 - } + DepthEnd = 6000, + DateStart = System.DateTimeOffset.Now.AddMinutes(-1), + DateEnd = System.DateTimeOffset.Now, + } }; //act @@ -74,10 +78,11 @@ public class DetectorDrillingTests : DetectorDrilling { Operation = new DetectedOperation { - IdReasonOfEnd = IdReasonOfEnd_PressureIsLo, DepthStart = 5000, - DepthEnd = 5000 - } + DepthEnd = 5000, + DateStart = System.DateTimeOffset.Now.AddMinutes(-1), + DateEnd = System.DateTimeOffset.Now, + } }; //act diff --git a/AsbCloudWebApi.Tests/UnitTests/Background/PeriodicBackgroundWorkerTest.cs b/AsbCloudWebApi.Tests/UnitTests/Background/PeriodicBackgroundWorkerTest.cs index 13c5f142..fced6864 100644 --- a/AsbCloudWebApi.Tests/UnitTests/Background/PeriodicBackgroundWorkerTest.cs +++ b/AsbCloudWebApi.Tests/UnitTests/Background/PeriodicBackgroundWorkerTest.cs @@ -86,7 +86,7 @@ public class PeriodicBackgroundWorkerTest service.Add(badWork, TimeSpan.FromSeconds(2)); service.Add(goodWork, TimeSpan.FromSeconds(2)); - await Task.Delay(TimeSpan.FromMilliseconds(20)); + await Task.Delay(TimeSpan.FromMilliseconds(128)); //assert Assert.Equal(expectadResult, result); diff --git a/AsbCloudWebApi/Controllers/WellOperationController.cs b/AsbCloudWebApi/Controllers/WellOperationController.cs index 45db3f0e..d4723657 100644 --- a/AsbCloudWebApi/Controllers/WellOperationController.cs +++ b/AsbCloudWebApi/Controllers/WellOperationController.cs @@ -16,6 +16,8 @@ using AsbCloudApp.Data.WellOperationImport; using AsbCloudApp.Services.WellOperationImport; using AsbCloudApp.Data.WellOperationImport.Options; using AsbCloudApp.Exceptions; +using AsbCloudDb.Model; +using AsbCloudInfrastructure; namespace AsbCloudWebApi.Controllers { @@ -266,7 +268,7 @@ namespace AsbCloudWebApi.Controllers if (!await CanUserEditWellOperationsAsync(idWell, cancellationToken)) return Forbid(); - if (deleteBeforeInsert && wellOperations.Any()) + if (deleteBeforeInsert) { var existingOperations = await operationRepository.GetAsync(new WellOperationRequest { @@ -344,46 +346,85 @@ namespace AsbCloudWebApi.Controllers return Ok(result); } - /// - /// Импорт операций из excel (xlsx) файла. Стандартный заполненный шаблон + /// Импорт фактических операций из excel (xlsx) файла. Стандартный заполненный шаблон + /// + /// id скважины + /// Коллекция из одного файла xlsx + /// Удалить операции перед сохранением + /// + /// + [HttpPost("import/fact/default/{deleteBeforeInsert:bool}")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [Permission] + public Task ImportFactDefaultExcelFileAsync(int idWell, + [FromForm] IFormFileCollection files, + bool deleteBeforeInsert, + CancellationToken cancellationToken) + { + var options = new WellOperationImportDefaultOptionsDto + { + IdType = WellOperation.IdOperationTypeFact + }; + + return ImportExcelFileAsync(idWell, files, options, + (stream, _) => wellOperationDefaultExcelParser.Parse(stream, options), + deleteBeforeInsert, + cancellationToken); + } + + /// + /// Импорт плановых операций из excel (xlsx) файла. Стандартный заполненный шаблон /// /// id скважины - /// Параметры для парсинга файла /// Коллекция из одного файла xlsx /// /// - [HttpPost("import/default")] + [HttpPost("import/plan/default")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status204NoContent)] [Permission] - public Task ImportDefaultExcelFileAsync(int idWell, - [FromQuery] WellOperationImportDefaultOptionsDto options, + public Task ImportPlanDefaultExcelFileAsync(int idWell, [FromForm] IFormFileCollection files, - CancellationToken cancellationToken) => ImportExcelFileAsync(idWell, files, options, - (stream, _) => wellOperationDefaultExcelParser.Parse(stream, options), - cancellationToken); + CancellationToken cancellationToken) + { + var options = new WellOperationImportDefaultOptionsDto + { + IdType = WellOperation.IdOperationTypePlan + }; + + return ImportExcelFileAsync(idWell, files, options, + (stream, _) => wellOperationDefaultExcelParser.Parse(stream, options), + null, + cancellationToken); + } /// /// Импорт операций из excel (xlsx) файла. ГПНХ (Хантос) /// /// id скважины - /// Параметры для парсинга файла /// Коллекция из одного файла xlsx /// /// - [HttpPost("import/gazpromKhantos")] + [HttpPost("import/plan/gazpromKhantos")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status204NoContent)] [Permission] - public Task ImportGazpromKhantosExcelFileAsync(int idWell, - [FromQuery] WellOperationImportGazpromKhantosOptionsDto options, + public Task ImportPlanGazpromKhantosExcelFileAsync(int idWell, [FromForm] IFormFileCollection files, - CancellationToken cancellationToken) => ImportExcelFileAsync(idWell, files, options, - (stream, _) => wellOperationGazpromKhantosExcelParser.Parse(stream, options), - cancellationToken); + CancellationToken cancellationToken) + { + var options = new WellOperationImportGazpromKhantosOptionsDto + { + IdType = WellOperation.IdOperationTypePlan + }; + + return ImportExcelFileAsync(idWell, files, options, + (stream, _) => wellOperationGazpromKhantosExcelParser.Parse(stream, options), + null, + cancellationToken); + } /// /// Создает excel файл с операциями по скважине @@ -451,9 +492,11 @@ namespace AsbCloudWebApi.Controllers return File(stream, "application/octet-stream", fileName); } + //TODO: deleteBeforeInsert тоже быстрый костыль private async Task ImportExcelFileAsync(int idWell, [FromForm] IFormFileCollection files, TOptions options, Func parseMethod, + bool? deleteBeforeInsert, CancellationToken cancellationToken) where TOptions : IWellOperationImportOptions { @@ -488,13 +531,22 @@ namespace AsbCloudWebApi.Controllers var wellOperations = wellOperationImportService.Import(idWell, idUser.Value, options.IdType, sheet) .OrderBy(w => w.DateStart); - var dateStart = wellOperations.Min(w => w.DateStart); + var dateStart = wellOperations.MinOrDefault(w => w.DateStart); foreach (var wellOperation in wellOperations) - wellOperation.Day = (wellOperation.DateStart - dateStart).TotalDays; - - if (!wellOperations.Any()) - return NoContent(); + { + if (dateStart.HasValue) + wellOperation.Day = (wellOperation.DateStart - dateStart.Value).TotalDays; + } + + //TODO: очень быстрый костыль + if (deleteBeforeInsert is not null && options.IdType == WellOperation.IdOperationTypeFact) + { + return await InsertRangeAsync(idWell, options.IdType, + deleteBeforeInsert.Value, + wellOperations, + cancellationToken); + } return Ok(wellOperations); } diff --git a/AsbCloudWebApi/DependencyInjection.cs b/AsbCloudWebApi/DependencyInjection.cs index 788c661d..8f25ee81 100644 --- a/AsbCloudWebApi/DependencyInjection.cs +++ b/AsbCloudWebApi/DependencyInjection.cs @@ -81,7 +81,8 @@ namespace AsbCloudWebApi c.IncludeXmlComments(xmlPath, includeControllerXmlComment); c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "AsbCloudApp.xml"), includeControllerXmlComment); - c.AddSignalRSwaggerGen(options => { + c.AddSignalRSwaggerGen(options => + { options.DisplayInDocument("signalr"); options.UseHubXmlCommentsSummaryAsTagDescription = true; options.UseHubXmlCommentsSummaryAsTag = true; diff --git a/AsbCloudWebApi/Program.cs b/AsbCloudWebApi/Program.cs index 864b9600..e1b6445b 100644 --- a/AsbCloudWebApi/Program.cs +++ b/AsbCloudWebApi/Program.cs @@ -7,8 +7,6 @@ namespace AsbCloudWebApi { // Uncomment next line to find wired exceptions by tracing. //static TraceListenerView trace4debug = new TraceListenerView(); - enum A { a = 1 << 2 } - public static void Main(string[] args) { var host = CreateHostBuilder(args).Build();