From a542bfb7f4ceee8b49cf8fcadf64ffb72cc97da5 Mon Sep 17 00:00:00 2001 From: Serghei Cebotari Date: Tue, 14 Jan 2025 14:01:01 +0000 Subject: [PATCH] Common Parsing Method --- .../Abstractions/ISkuParser.cs | 8 ++++++ ...Controller.cs => CommonParseController.cs} | 18 ++++++------ RhSolutions.SkuParser.Api/Program.cs | 7 +++-- .../{CsvParser.cs => CommonCsvParser.cs} | 10 ++++--- .../{ExcelParser.cs => CommonExcelParser.cs} | 28 +++++++++++-------- .../Services/ISkuParser.cs | 7 ----- .../ExcelParserTests.cs | 28 +++++++++---------- 7 files changed, 56 insertions(+), 50 deletions(-) create mode 100644 RhSolutions.SkuParser.Api/Abstractions/ISkuParser.cs rename RhSolutions.SkuParser.Api/Controllers/{ProductsController.cs => CommonParseController.cs} (56%) rename RhSolutions.SkuParser.Api/Services/{CsvParser.cs => CommonCsvParser.cs} (61%) rename RhSolutions.SkuParser.Api/Services/{ExcelParser.cs => CommonExcelParser.cs} (78%) delete mode 100644 RhSolutions.SkuParser.Api/Services/ISkuParser.cs diff --git a/RhSolutions.SkuParser.Api/Abstractions/ISkuParser.cs b/RhSolutions.SkuParser.Api/Abstractions/ISkuParser.cs new file mode 100644 index 0000000..a1b2fbf --- /dev/null +++ b/RhSolutions.SkuParser.Api/Abstractions/ISkuParser.cs @@ -0,0 +1,8 @@ +using RhSolutions.SkuParser.Models; + +namespace RhSolutions.SkuParser.Abstractions; + +public interface ISkuParser +{ + public Dictionary ParseProducts(IFormFile file); +} diff --git a/RhSolutions.SkuParser.Api/Controllers/ProductsController.cs b/RhSolutions.SkuParser.Api/Controllers/CommonParseController.cs similarity index 56% rename from RhSolutions.SkuParser.Api/Controllers/ProductsController.cs rename to RhSolutions.SkuParser.Api/Controllers/CommonParseController.cs index 77b277b..c819851 100644 --- a/RhSolutions.SkuParser.Api/Controllers/ProductsController.cs +++ b/RhSolutions.SkuParser.Api/Controllers/CommonParseController.cs @@ -1,16 +1,16 @@ using Microsoft.AspNetCore.Mvc; using RhSolutions.SkuParser.Models; -using RhSolutions.SkuParser.Services; +using RhSolutions.SkuParser.Abstractions; namespace RhSolutions.SkuParser.Controllers; [ApiController] [Route("/api/[controller]")] -public class ProductsController : ControllerBase +public class CommonParseController : ControllerBase { private IServiceProvider _provider; private Dictionary _result; - public ProductsController(IServiceProvider provider) + public CommonParseController(IServiceProvider provider) { _provider = provider; _result = new(); @@ -25,16 +25,16 @@ public class ProductsController : ControllerBase foreach (var file in files) { ISkuParser parser = _provider.GetRequiredKeyedService(file.ContentType); - IEnumerable productQuantities = parser.ParseProducts(file); - foreach (ProductQuantity pq in productQuantities) + var dict = parser.ParseProducts(file); + foreach (var kvp in dict) { - if (_result.ContainsKey(pq.Product)) + if (_result.ContainsKey(kvp.Key)) { - _result[pq.Product] += pq.Quantity; + _result[kvp.Key] += kvp.Value; } else { - _result.Add(pq.Product, pq.Quantity); + _result.Add(kvp.Key, kvp.Value); } } } @@ -43,6 +43,6 @@ public class ProductsController : ControllerBase { return BadRequest(error: $"{ex.Message}\n\n{ex.Source}\n{ex.StackTrace}"); } - return new JsonResult(_result.Select(x => new { Sku = x.Key.ToString(), Quantity = x.Value })); + return new JsonResult(_result.Select(x => new { x.Key.Sku, x.Value })); } } \ No newline at end of file diff --git a/RhSolutions.SkuParser.Api/Program.cs b/RhSolutions.SkuParser.Api/Program.cs index e0acec8..13e0b90 100644 --- a/RhSolutions.SkuParser.Api/Program.cs +++ b/RhSolutions.SkuParser.Api/Program.cs @@ -1,10 +1,11 @@ +using RhSolutions.SkuParser.Abstractions; using RhSolutions.SkuParser.Services; var builder = WebApplication.CreateBuilder(args); builder.Services - .AddKeyedScoped("text/csv") - .AddKeyedScoped("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") - .AddKeyedScoped("application/vnd.ms-excel.sheet.macroenabled.12"); + .AddKeyedScoped("text/csv") + .AddKeyedScoped("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + .AddKeyedScoped("application/vnd.ms-excel.sheet.macroenabled.12"); builder.Services.AddControllers(); var app = builder.Build(); diff --git a/RhSolutions.SkuParser.Api/Services/CsvParser.cs b/RhSolutions.SkuParser.Api/Services/CommonCsvParser.cs similarity index 61% rename from RhSolutions.SkuParser.Api/Services/CsvParser.cs rename to RhSolutions.SkuParser.Api/Services/CommonCsvParser.cs index 2776721..e88ba25 100644 --- a/RhSolutions.SkuParser.Api/Services/CsvParser.cs +++ b/RhSolutions.SkuParser.Api/Services/CommonCsvParser.cs @@ -1,6 +1,7 @@ using System.Globalization; using CsvHelper; using CsvHelper.Configuration; +using RhSolutions.SkuParser.Abstractions; using RhSolutions.SkuParser.Models; namespace RhSolutions.SkuParser.Services; @@ -8,9 +9,9 @@ namespace RhSolutions.SkuParser.Services; /// /// Парсер артикулов и их количества из файлов *.csv /// -public class CsvParser : ISkuParser +public class CommonCsvParser : ISkuParser { - public IEnumerable ParseProducts(IFormFile file) + public Dictionary ParseProducts(IFormFile file) { using StreamReader reader = new(file.OpenReadStream()); var config = new CsvConfiguration(CultureInfo.GetCultureInfo("ru-RU")) @@ -18,7 +19,8 @@ public class CsvParser : ISkuParser HasHeaderRecord = false, }; using CsvReader csvReader = new(reader, config); - - return csvReader.GetRecords().ToList(); + + return csvReader.GetRecords() + .ToDictionary(pq => new Product() { Sku = pq.Product.Sku }, pq => pq.Quantity); } } diff --git a/RhSolutions.SkuParser.Api/Services/ExcelParser.cs b/RhSolutions.SkuParser.Api/Services/CommonExcelParser.cs similarity index 78% rename from RhSolutions.SkuParser.Api/Services/ExcelParser.cs rename to RhSolutions.SkuParser.Api/Services/CommonExcelParser.cs index fec3885..206200d 100644 --- a/RhSolutions.SkuParser.Api/Services/ExcelParser.cs +++ b/RhSolutions.SkuParser.Api/Services/CommonExcelParser.cs @@ -1,11 +1,12 @@ using ClosedXML.Excel; +using RhSolutions.SkuParser.Abstractions; using RhSolutions.SkuParser.Models; namespace RhSolutions.SkuParser.Services; -public class ExcelParser : ISkuParser +public class CommonExcelParser : ISkuParser { - public IEnumerable ParseProducts(IFormFile file) + public Dictionary ParseProducts(IFormFile file) { using XLWorkbook workbook = new(file.OpenReadStream()); IXLWorksheet ws = workbook.Worksheet(1); @@ -51,23 +52,26 @@ public class ExcelParser : ISkuParser throw new ArgumentException($"Столбец с количеством не определен: {file.FileName}"); } - List result = new(); + Dictionary result = new(); var rows = quantityColumn.CellsUsed().Select(x => x.Address.RowNumber); - + foreach (var row in rows) { var quantity = quantityColumn.Cell(row).Value; var sku = skuColumn.Cell(row).Value; - - if (quantity.IsNumber - && Product.TryParse(sku.ToString(), out Product? p)) + + if (quantity.IsNumber + && Product.TryParse(sku.ToString(), out Product? p) + && p != null) { - ProductQuantity pq = new() + if (result.ContainsKey(p)) { - Product = p!, - Quantity = quantity.GetNumber() - }; - result.Add(pq); + result[p] += (double)quantity; + } + else + { + result.Add(p, (double)quantity); + } } } diff --git a/RhSolutions.SkuParser.Api/Services/ISkuParser.cs b/RhSolutions.SkuParser.Api/Services/ISkuParser.cs deleted file mode 100644 index 4329135..0000000 --- a/RhSolutions.SkuParser.Api/Services/ISkuParser.cs +++ /dev/null @@ -1,7 +0,0 @@ -using RhSolutions.SkuParser.Models; - -namespace RhSolutions.SkuParser.Services; -public interface ISkuParser -{ - public IEnumerable ParseProducts(IFormFile file); -} diff --git a/RhSolutions.SkuParser.Tests/ExcelParserTests.cs b/RhSolutions.SkuParser.Tests/ExcelParserTests.cs index 60e1e7b..781ea56 100644 --- a/RhSolutions.SkuParser.Tests/ExcelParserTests.cs +++ b/RhSolutions.SkuParser.Tests/ExcelParserTests.cs @@ -4,18 +4,18 @@ namespace RhSolutions.SkuParser.Tests; public class ExcelParserTests { - private static readonly List _expected = new() + private static readonly Dictionary _expected = new() { - new ProductQuantity() {Product= new Product() {Sku = "11303703100"}, Quantity = 2129.5}, - new ProductQuantity() {Product= new Product() {Sku = "11303803100"}, Quantity = 503}, - new ProductQuantity() {Product= new Product() {Sku = "11303903050"}, Quantity = 52}, - new ProductQuantity() {Product= new Product() {Sku = "11080011001"}, Quantity = 2154}, - new ProductQuantity() {Product= new Product() {Sku = "11080021001"}, Quantity = 134}, - new ProductQuantity() {Product= new Product() {Sku = "11080031001"}, Quantity = 6}, - new ProductQuantity() {Product= new Product() {Sku = "11080311001"}, Quantity = 462}, - new ProductQuantity() {Product= new Product() {Sku = "11080611001"}, Quantity = 38}, - new ProductQuantity() {Product= new Product() {Sku = "11080811001"}, Quantity = 24}, - new ProductQuantity() {Product= new Product() {Sku = "11080831001"}, Quantity = 2}, + [new Product() {Sku = "11303703100"}] = 2129.5, + [new Product() {Sku = "11303803100"}] = 503, + [new Product() {Sku = "11303903050"}] = 52, + [new Product() {Sku = "11080011001"}] = 2154, + [new Product() {Sku = "11080021001"}] = 134, + [new Product() {Sku = "11080031001"}] = 6, + [new Product() {Sku = "11080311001"}] = 462, + [new Product() {Sku = "11080611001"}] = 38, + [new Product() {Sku = "11080811001"}] = 24, + [new Product() {Sku = "11080831001"}] = 2, }; [TestCase("simple.xlsx")] @@ -28,10 +28,9 @@ public class ExcelParserTests public void XlsxTests(string filename) { var mockFile = FormFileUtil.GetMockFormFile(filename); - var parser = new ExcelParser(); + var parser = new CommonExcelParser(); var actual = parser.ParseProducts(mockFile.Object); Assert.That(actual.Count, Is.EqualTo(_expected.Count())); - CollectionAssert.AllItemsAreInstancesOfType(actual, typeof(ProductQuantity)); CollectionAssert.AreEqual(_expected, actual); } @@ -39,10 +38,9 @@ public class ExcelParserTests public void CsvTests(string filename) { var mockFile = FormFileUtil.GetMockFormFile(filename); - var parser = new CsvParser(); + var parser = new CommonCsvParser(); var actual = parser.ParseProducts(mockFile.Object); Assert.That(actual.Count, Is.EqualTo(_expected.Count())); - CollectionAssert.AllItemsAreInstancesOfType(actual, typeof(ProductQuantity)); CollectionAssert.AreEqual(_expected, actual); } }