1
0

Common Parsing Method

This commit is contained in:
Serghei Cebotari 2025-01-14 14:01:01 +00:00
parent e2ac0c46c1
commit a542bfb7f4
7 changed files with 56 additions and 50 deletions

View File

@ -0,0 +1,8 @@
using RhSolutions.SkuParser.Models;
namespace RhSolutions.SkuParser.Abstractions;
public interface ISkuParser
{
public Dictionary<Product, double> ParseProducts(IFormFile file);
}

View File

@ -1,16 +1,16 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using RhSolutions.SkuParser.Models; using RhSolutions.SkuParser.Models;
using RhSolutions.SkuParser.Services; using RhSolutions.SkuParser.Abstractions;
namespace RhSolutions.SkuParser.Controllers; namespace RhSolutions.SkuParser.Controllers;
[ApiController] [ApiController]
[Route("/api/[controller]")] [Route("/api/[controller]")]
public class ProductsController : ControllerBase public class CommonParseController : ControllerBase
{ {
private IServiceProvider _provider; private IServiceProvider _provider;
private Dictionary<Product, double> _result; private Dictionary<Product, double> _result;
public ProductsController(IServiceProvider provider) public CommonParseController(IServiceProvider provider)
{ {
_provider = provider; _provider = provider;
_result = new(); _result = new();
@ -25,16 +25,16 @@ public class ProductsController : ControllerBase
foreach (var file in files) foreach (var file in files)
{ {
ISkuParser parser = _provider.GetRequiredKeyedService<ISkuParser>(file.ContentType); ISkuParser parser = _provider.GetRequiredKeyedService<ISkuParser>(file.ContentType);
IEnumerable<ProductQuantity> productQuantities = parser.ParseProducts(file); var dict = parser.ParseProducts(file);
foreach (ProductQuantity pq in productQuantities) 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 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 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 }));
} }
} }

View File

@ -1,10 +1,11 @@
using RhSolutions.SkuParser.Abstractions;
using RhSolutions.SkuParser.Services; using RhSolutions.SkuParser.Services;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services builder.Services
.AddKeyedScoped<ISkuParser, CsvParser>("text/csv") .AddKeyedScoped<ISkuParser, CommonCsvParser>("text/csv")
.AddKeyedScoped<ISkuParser, ExcelParser>("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") .AddKeyedScoped<ISkuParser, CommonExcelParser>("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
.AddKeyedScoped<ISkuParser, ExcelParser>("application/vnd.ms-excel.sheet.macroenabled.12"); .AddKeyedScoped<ISkuParser, CommonExcelParser>("application/vnd.ms-excel.sheet.macroenabled.12");
builder.Services.AddControllers(); builder.Services.AddControllers();
var app = builder.Build(); var app = builder.Build();

View File

@ -1,6 +1,7 @@
using System.Globalization; using System.Globalization;
using CsvHelper; using CsvHelper;
using CsvHelper.Configuration; using CsvHelper.Configuration;
using RhSolutions.SkuParser.Abstractions;
using RhSolutions.SkuParser.Models; using RhSolutions.SkuParser.Models;
namespace RhSolutions.SkuParser.Services; namespace RhSolutions.SkuParser.Services;
@ -8,9 +9,9 @@ namespace RhSolutions.SkuParser.Services;
/// <summary> /// <summary>
/// Парсер артикулов и их количества из файлов *.csv /// Парсер артикулов и их количества из файлов *.csv
/// </summary> /// </summary>
public class CsvParser : ISkuParser public class CommonCsvParser : ISkuParser
{ {
public IEnumerable<ProductQuantity> ParseProducts(IFormFile file) public Dictionary<Product, double> ParseProducts(IFormFile file)
{ {
using StreamReader reader = new(file.OpenReadStream()); using StreamReader reader = new(file.OpenReadStream());
var config = new CsvConfiguration(CultureInfo.GetCultureInfo("ru-RU")) var config = new CsvConfiguration(CultureInfo.GetCultureInfo("ru-RU"))
@ -18,7 +19,8 @@ public class CsvParser : ISkuParser
HasHeaderRecord = false, HasHeaderRecord = false,
}; };
using CsvReader csvReader = new(reader, config); using CsvReader csvReader = new(reader, config);
return csvReader.GetRecords<ProductQuantity>().ToList(); return csvReader.GetRecords<ProductQuantity>()
.ToDictionary(pq => new Product() { Sku = pq.Product.Sku }, pq => pq.Quantity);
} }
} }

View File

@ -1,11 +1,12 @@
using ClosedXML.Excel; using ClosedXML.Excel;
using RhSolutions.SkuParser.Abstractions;
using RhSolutions.SkuParser.Models; using RhSolutions.SkuParser.Models;
namespace RhSolutions.SkuParser.Services; namespace RhSolutions.SkuParser.Services;
public class ExcelParser : ISkuParser public class CommonExcelParser : ISkuParser
{ {
public IEnumerable<ProductQuantity> ParseProducts(IFormFile file) public Dictionary<Product, double> ParseProducts(IFormFile file)
{ {
using XLWorkbook workbook = new(file.OpenReadStream()); using XLWorkbook workbook = new(file.OpenReadStream());
IXLWorksheet ws = workbook.Worksheet(1); IXLWorksheet ws = workbook.Worksheet(1);
@ -51,23 +52,26 @@ public class ExcelParser : ISkuParser
throw new ArgumentException($"Столбец с количеством не определен: {file.FileName}"); throw new ArgumentException($"Столбец с количеством не определен: {file.FileName}");
} }
List<ProductQuantity> result = new(); Dictionary<Product, double> result = new();
var rows = quantityColumn.CellsUsed().Select(x => x.Address.RowNumber); var rows = quantityColumn.CellsUsed().Select(x => x.Address.RowNumber);
foreach (var row in rows) foreach (var row in rows)
{ {
var quantity = quantityColumn.Cell(row).Value; var quantity = quantityColumn.Cell(row).Value;
var sku = skuColumn.Cell(row).Value; var sku = skuColumn.Cell(row).Value;
if (quantity.IsNumber if (quantity.IsNumber
&& Product.TryParse(sku.ToString(), out Product? p)) && Product.TryParse(sku.ToString(), out Product? p)
&& p != null)
{ {
ProductQuantity pq = new() if (result.ContainsKey(p))
{ {
Product = p!, result[p] += (double)quantity;
Quantity = quantity.GetNumber() }
}; else
result.Add(pq); {
result.Add(p, (double)quantity);
}
} }
} }

View File

@ -1,7 +0,0 @@
using RhSolutions.SkuParser.Models;
namespace RhSolutions.SkuParser.Services;
public interface ISkuParser
{
public IEnumerable<ProductQuantity> ParseProducts(IFormFile file);
}

View File

@ -4,18 +4,18 @@ namespace RhSolutions.SkuParser.Tests;
public class ExcelParserTests public class ExcelParserTests
{ {
private static readonly List<ProductQuantity> _expected = new() private static readonly Dictionary<Product, double> _expected = new()
{ {
new ProductQuantity() {Product= new Product() {Sku = "11303703100"}, Quantity = 2129.5}, [new Product() {Sku = "11303703100"}] = 2129.5,
new ProductQuantity() {Product= new Product() {Sku = "11303803100"}, Quantity = 503}, [new Product() {Sku = "11303803100"}] = 503,
new ProductQuantity() {Product= new Product() {Sku = "11303903050"}, Quantity = 52}, [new Product() {Sku = "11303903050"}] = 52,
new ProductQuantity() {Product= new Product() {Sku = "11080011001"}, Quantity = 2154}, [new Product() {Sku = "11080011001"}] = 2154,
new ProductQuantity() {Product= new Product() {Sku = "11080021001"}, Quantity = 134}, [new Product() {Sku = "11080021001"}] = 134,
new ProductQuantity() {Product= new Product() {Sku = "11080031001"}, Quantity = 6}, [new Product() {Sku = "11080031001"}] = 6,
new ProductQuantity() {Product= new Product() {Sku = "11080311001"}, Quantity = 462}, [new Product() {Sku = "11080311001"}] = 462,
new ProductQuantity() {Product= new Product() {Sku = "11080611001"}, Quantity = 38}, [new Product() {Sku = "11080611001"}] = 38,
new ProductQuantity() {Product= new Product() {Sku = "11080811001"}, Quantity = 24}, [new Product() {Sku = "11080811001"}] = 24,
new ProductQuantity() {Product= new Product() {Sku = "11080831001"}, Quantity = 2}, [new Product() {Sku = "11080831001"}] = 2,
}; };
[TestCase("simple.xlsx")] [TestCase("simple.xlsx")]
@ -28,10 +28,9 @@ public class ExcelParserTests
public void XlsxTests(string filename) public void XlsxTests(string filename)
{ {
var mockFile = FormFileUtil.GetMockFormFile(filename); var mockFile = FormFileUtil.GetMockFormFile(filename);
var parser = new ExcelParser(); var parser = new CommonExcelParser();
var actual = parser.ParseProducts(mockFile.Object); var actual = parser.ParseProducts(mockFile.Object);
Assert.That(actual.Count, Is.EqualTo(_expected.Count())); Assert.That(actual.Count, Is.EqualTo(_expected.Count()));
CollectionAssert.AllItemsAreInstancesOfType(actual, typeof(ProductQuantity));
CollectionAssert.AreEqual(_expected, actual); CollectionAssert.AreEqual(_expected, actual);
} }
@ -39,10 +38,9 @@ public class ExcelParserTests
public void CsvTests(string filename) public void CsvTests(string filename)
{ {
var mockFile = FormFileUtil.GetMockFormFile(filename); var mockFile = FormFileUtil.GetMockFormFile(filename);
var parser = new CsvParser(); var parser = new CommonCsvParser();
var actual = parser.ParseProducts(mockFile.Object); var actual = parser.ParseProducts(mockFile.Object);
Assert.That(actual.Count, Is.EqualTo(_expected.Count())); Assert.That(actual.Count, Is.EqualTo(_expected.Count()));
CollectionAssert.AllItemsAreInstancesOfType(actual, typeof(ProductQuantity));
CollectionAssert.AreEqual(_expected, actual); CollectionAssert.AreEqual(_expected, actual);
} }
} }