Common Parsing Method
This commit is contained in:
parent
e2ac0c46c1
commit
a542bfb7f4
8
RhSolutions.SkuParser.Api/Abstractions/ISkuParser.cs
Normal file
8
RhSolutions.SkuParser.Api/Abstractions/ISkuParser.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
using RhSolutions.SkuParser.Models;
|
||||||
|
|
||||||
|
namespace RhSolutions.SkuParser.Abstractions;
|
||||||
|
|
||||||
|
public interface ISkuParser
|
||||||
|
{
|
||||||
|
public Dictionary<Product, double> ParseProducts(IFormFile 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 }));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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();
|
||||||
|
@ -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"))
|
||||||
@ -19,6 +20,7 @@ public class CsvParser : ISkuParser
|
|||||||
};
|
};
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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,7 +52,7 @@ 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)
|
||||||
@ -60,14 +61,17 @@ public class ExcelParser : ISkuParser
|
|||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
using RhSolutions.SkuParser.Models;
|
|
||||||
|
|
||||||
namespace RhSolutions.SkuParser.Services;
|
|
||||||
public interface ISkuParser
|
|
||||||
{
|
|
||||||
public IEnumerable<ProductQuantity> ParseProducts(IFormFile 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user