1
0

Implement BS parser

This commit is contained in:
Serghei Cebotari 2025-01-15 15:06:04 +03:00
parent a542bfb7f4
commit be127319e2
7 changed files with 157 additions and 62 deletions

View File

@ -6,26 +6,28 @@ namespace RhSolutions.SkuParser.Controllers;
[ApiController] [ApiController]
[Route("/api/[controller]")] [Route("/api/[controller]")]
public class CommonParseController : ControllerBase public class ParseController : ControllerBase
{ {
private IServiceProvider _provider; private IServiceProvider _provider;
private Dictionary<Product, double> _result; private Dictionary<Product, double> _result;
public CommonParseController(IServiceProvider provider) public ParseController(IServiceProvider provider)
{ {
_provider = provider; _provider = provider;
_result = new(); _result = new();
} }
[HttpPost] [HttpPost]
public IActionResult PostFiles() public IActionResult PostFiles([FromQuery] bool bs = false)
{ {
IFormFileCollection files = Request.Form.Files; IFormFileCollection files = Request.Form.Files;
try try
{ {
foreach (var file in files) foreach (var file in files)
{ {
ISkuParser parser = _provider.GetRequiredKeyedService<ISkuParser>(file.ContentType); ISkuParser parser = bs ? _provider.GetRequiredKeyedService<ISkuParser>("BS")
: _provider.GetRequiredKeyedService<ISkuParser>(file.ContentType);
var dict = parser.ParseProducts(file); var dict = parser.ParseProducts(file);
foreach (var kvp in dict) foreach (var kvp in dict)
{ {
if (_result.ContainsKey(kvp.Key)) if (_result.ContainsKey(kvp.Key))
@ -43,6 +45,13 @@ public class CommonParseController : 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 { x.Key.Sku, x.Value })); return new JsonResult(_result.Select(x => new
{
x.Key.Sku,
x.Key.ProductLine,
x.Key.Name,
x.Key.Price,
quantity = x.Value
}));
} }
} }

View File

@ -4,6 +4,10 @@ namespace RhSolutions.SkuParser.Models;
public record Product public record Product
{ {
private string _sku = string.Empty;
private const string _parsePattern = @"(?<Lead>[1\s]|^|\b)(?<Article>\d{6})(?<Delimiter>[\s13-])(?<Variant>\d{3})(\b|$)";
private const string _validnessPattern = @"^1\d{6}[1|3]\d{3}$";
/// <summary> /// <summary>
/// Артикул РЕХАУ в заданном формате /// Артикул РЕХАУ в заданном формате
/// </summary> /// </summary>
@ -17,9 +21,9 @@ public record Product
: throw new ArgumentException("$Неверный артикул: {value}"); : throw new ArgumentException("$Неверный артикул: {value}");
} }
} }
private string _sku = string.Empty; public ProductLine? ProductLine { get; set; }
private const string _parsePattern = @"(?<Lead>[1\s]|^|\b)(?<Article>\d{6})(?<Delimiter>[\s13-])(?<Variant>\d{3})(\b|$)"; public string? Name { get; set; }
private const string _validnessPattern = @"^1\d{6}[1|3]\d{3}$"; public decimal? Price { get; set; }
private static bool IsValudSku(string value) private static bool IsValudSku(string value)
{ {

View File

@ -0,0 +1,7 @@
namespace RhSolutions.SkuParser.Models;
public enum ProductLine
{
RAUTITAN,
RAUTHERMS
}

View File

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

View File

@ -0,0 +1,72 @@
using System.Data;
using ClosedXML.Excel;
using RhSolutions.SkuParser.Abstractions;
using RhSolutions.SkuParser.Models;
namespace RhSolutions.SkuParser.Api.Services;
public class BsExcelParser : ISkuParser
{
private IConfiguration configuration;
// private Dictionary<Product, double> result;
private const int rowsLookupCount = 20;
private const decimal vat = 1.2M;
public BsExcelParser(IConfiguration configuration)
{
this.configuration = configuration;
// result = new();
}
public Dictionary<Product, double> ParseProducts(IFormFile file)
{
using XLWorkbook workbook = new(file.OpenReadStream());
IXLWorksheet ws = workbook.Worksheet(1);
var headers = configuration.GetSection("Headers");
ws.AutoFilter.Clear();
var cells = configuration.GetSection("Headers")
.GetChildren()
.ToDictionary(x => x.Path, x => ws.Range(1, 1, rowsLookupCount, ws.LastCellUsed().Address.ColumnNumber)
.Search(x.Value)
.FirstOrDefault())
.OrderBy(x => x.Value?.Address.ColumnNumber ?? int.MaxValue);
if (cells == null || cells.Any(x => x.Value == null))
{
throw new DataException($"В рабочей книге отсуствует таблица с необходимыми заголовками: {file.Name}");
}
var firstCell = cells.First().Value;
var lastCell = cells.Last().Value?.WorksheetColumn().LastCellUsed();
var table = ws.Range(firstCell, lastCell).AsTable();
var rows = table.DataRange.Rows();
Dictionary<Product, double> result = new();
foreach (var row in rows.Where(r => r.Field(headers["Quantity"]).Value.IsNumber))
{
Product product = new()
{
Sku = row.Field(headers["Sku"]).GetString(),
ProductLine = row.Field(headers["ProductLine"]).GetString() switch
{
"RAUTITAN" => ProductLine.RAUTITAN,
"RAUTHERM S" => ProductLine.RAUTHERMS,
_ => null
},
Name = row.Field(headers["Name"]).GetString(),
Price = decimal.TryParse(row.Field(headers["Price"]).GetString(), out decimal value)
? Math.Round(value * vat, 2)
: null
};
double quantity = double.TryParse(row.Field(headers["Quantity"]).GetString(), out double q) ? q : 0;
if (result.ContainsKey(product))
{
result[product] += quantity;
}
else
{
result.Add(product, quantity);
}
}
return result;
}
}

View File

@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -5,5 +5,12 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"AllowedHosts": "*" "AllowedHosts": "*",
"Headers": {
"ProductLine": "Программа",
"Sku": "Актуальный материал",
"Name": "Наименование",
"Quantity": "Кол-во",
"Price": "Цена брутто \nЕВРО\nбез НДС"
}
} }