Socio, esto furula
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
linuxct 2020-07-14 12:15:44 +02:00
parent 0da67c4717
commit 7ef04f118b
14 changed files with 373 additions and 54 deletions

View File

@ -0,0 +1,106 @@
namespace OD.WebApplication.Context
{
public static class ApplicationContextHelper
{
public static string[] BusinessApplicationCategories =
{
"Metrónomos, afinadores, atriles, accesorios y complementos en general",
"Contrabajo y arcos",
"Cuerdas de violín, viola, violonchelo y contrabajo",
"Pastillas, afinadores, puentes, cordales, soportes y accesorios de cuerda",
"Viola y arcos de viola",
"Violín y arcos de violín",
"Violonchelo y arcos",
"Amplificadores, cabezales y bafles de guitarra y bajo",
"Bajos eléctricos y acústicos",
"Bandurrias",
"Cuerdas de instrumentos de cuerda punteada",
"Estuches, fundas y soportes para instrumentos de cuerda punteada",
"Guitarras acústicas amplificadas",
"Guitarras acústicas no amplificadas",
"Guitarras eléctricas",
"Guitarras españolas amplificadas",
"Guitarras españolas no amplificadas",
"Laúdes",
"Procesadores de guitarra, pedales, pedaleras, pastillas, mocrófonos de guitara y afinadores",
"Púas, puentes, cejuelas, tornillos, cordales, clavijas y accesorios en general de guitarra e instrumentos de cuerda p",
"Timples, ukeleles y otros instrumentos de cuerda punteada",
"Lámaras, focos, procesadores y mesas iluminación",
"Líquido y máquinas de humo",
"Software secuenciador, editor, procesador y grabador de sonido y sus complementos, aso como programas en general",
"Torres, bolas y complementos de iluminación",
"Armónicas, acordeones e instrumentos de lengüeta y sus complementos",
"Baterías acústicas y digitales",
"Cajones rumberos, claves, sonajas y otros instrumentos no incluidos en otra familia",
"Marimbas, xilófonos, metalófonos e instrumentos con escala afinada",
"Platos",
"Soportes, racks, fundas, banquetas, parches, membranas, baquetas y accesorios de percusión en general",
"Timbales, bombos, tambores, cajas panderetas y otros instrumentos de membrana",
"Estuches, fundas, banquetas y accesorios de piano",
"Pianos de Cola Nuevos con o sin módulo",
"Pianos de Cola Usados",
"Pianos Digitales con ritmos",
"Pianos Digitales sin ritmos",
"Pianos Verticales Nuevos con o sin módulo",
"Pianos Verticales Usados con o sin módulo",
"Libretas, block y papel pautado",
"Libros y partituras de canto",
"Libros y partituras de clarinete",
"Libros y partituras de contrabajo",
"Libros y partituras de fagot",
"Libros y partituras de flauta, dulzaina y gaita",
"Libros y partituras de guitarra y cuerda punteada",
"Libros y partituras de música de cámara",
"Libros y partituras de oboe",
"Libros y partituras de orquesta",
"Libros y partituras de percusión",
"Libros y partituras de piano",
"Libros y partituras de saxo",
"Libros y partituras de solfeo, armonía, Historia de la Música y textos en general",
"Libros y partituras de teclado y acordeón",
"Libros y partituras de trombón, tuba y bombardino",
"Libros y partituras de trompa",
"Libros y partituras de trompeta",
"Libros y partituras de viola",
"Libros y partituras de violín",
"Libros y partituras de violonchelo",
"Alquiler de piano, teclados y otros aparatos",
"Servicios de afinación, reparación y transporte de pianos",
"Servicios de reparación de bafles, etapas y sistemas de amplificación y procesado de sonido",
"Servicios de reparación de teclados e instrumentos digitales",
"Servicios de reparación y ajuste de guitarras",
"Servicios de reparación y ajuste de instrumentos de cuerda",
"Servicios de reparación y ajuste de instrumentos de viento",
"Bafles, altavoces y monitores normales o inalámbricos",
"Cables y conectores",
"Etapas de potencia",
"Grabadores de sonido y sus soportes",
"Mesas de mezcla, cabezales, equipos integrados, amplificadores (combos) multiuso y mezcladores",
"Micrófonos normales e inalámbricos",
"Procesadores de efectos, pachbay, puertas de ruido, divisores, ecualizadores y otros sistemas de procesado de sonido",
"Soportes de micro, columna y torres, racks, auriculares y accesorios en general de amplificación",
"Accesorios de informática en general",
"CD y DVD",
"Librerías de sonidos y midifiles",
"OTRO",
"Tarjetas de sonido e interfaces de ordenador con o sin software",
"Alimentadores, fundas, estuches, pedales, soportes y accesorios de teclado y módulos",
"Cajas de ritmos y módulos de DJ",
"Módulos arregladores con ritmos y secuenciadores",
"Módulos de sonido, sintetizadores y samplers",
"Módulos digitales de percusión",
"Órganos y teclados litúrgicos",
"Teclados con ritmos",
"Teclados Sintetizadores, Workstation y samplers",
"Teclados y mesas controladores",
"Cañas, boquillas, abrazaderas, estuches, fundas, soportes, atriles, accesorios y complementos de viento",
"Clarinete",
"Flauta travesera y de pico",
"Oboe, fliscorno, bombardino, fagot, helicón, tuba y otros instrumentos de viento",
"Saxo",
"Trombón",
"Trompa",
"Trompetas y cornetas"
};
}
}

View File

@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Globalization;
using System.IO;
using AutoMapper;
using CsvHelper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using OD.WebApplication.Models;
@ -12,10 +14,12 @@ namespace OD.WebApplication.Controllers
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IMapper _mapper;
public HomeController(ILogger<HomeController> logger)
public HomeController(ILogger<HomeController> logger, IMapper mapper)
{
_logger = logger;
_mapper = mapper;
}
public IActionResult Index()
@ -23,21 +27,67 @@ namespace OD.WebApplication.Controllers
return View();
}
public IActionResult Upload()
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult UploadCSV(HomeViewModel model)
{
return View();
}
try
{
if (ModelState.IsValid)
{
using var reader = new StreamReader(model.FileAttach.OpenReadStream());
using var csvReader = new CsvReader(reader, CultureInfo.GetCultureInfo("es-ES"));
csvReader.Configuration.BadDataFound = null;
csvReader.Configuration.HeaderValidated = null;
csvReader.Configuration.MissingFieldFound = null;
csvReader.Configuration.Delimiter = ";";
var originDto = new List<OriginDataModel>();
csvReader.Read();
csvReader.ReadHeader();
while (csvReader.Read())
{
var record = new OriginDataModel
{
Id = csvReader.GetField("ID"),
Categories = csvReader.GetField<string>("Categorías"),
Manufacturer = csvReader.GetField<string>("Fabricante"),
PriceListingWithoutVAT = decimal.Parse(csvReader.GetField("Precio tiendas sin IVA")),
PricePublicWithoutVAT = decimal.Parse(csvReader.GetField("PVP sin IVA")),
PriceRecommendedWithoutVAT = decimal.Parse(csvReader.GetField("PVR sin IVA")),
Stock = decimal.Parse(csvReader.GetField("Stock")),
Volume = float.Parse(csvReader.GetField("Volumen")),
Name = csvReader.GetField("Nombre"),
LongDesc = csvReader.GetField("Descripción larga"),
ShortDesc = csvReader.GetField("Descripción breve"),
ImageUrls = csvReader.GetField("Imagen"),
Ean13 = csvReader.GetField("EAN13"),
Sda = csvReader.GetField("SDA")
};
originDto.Add(record);
}
var destinationDto = _mapper.Map<List<OriginDataModel>, List<DestinationDataModel>>(originDto);
var ms = new MemoryStream();
var sw = new StreamWriter(ms);
using var csvWriter = new CsvWriter(sw, CultureInfo.GetCultureInfo("es-ES"));
csvWriter.Configuration.Delimiter = ",";
csvWriter.WriteRecords(destinationDto);
sw.Flush();
ms.Position = 0;
return File(ms.ToArray(), "text/csv", "result.csv");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Attempted to perform an invalid conversion");
return new ErrorMessageResult(new ErrorResult { Exception = ex });
}
public async Task<IActionResult> UploadCSV()
{
return Ok();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{

View File

@ -0,0 +1,28 @@
using CsvHelper.Configuration.Attributes;
namespace OD.WebApplication.Models
{
public class DestinationDataModel
{
[Name("categ_id")]
public string Category { get; set; }
[Name("brand")]
public string Brand { get; set; }
[Name("name")]
public string Name { get; set; }
[Name("alternate_code")]
public string AlternateCode { get; set; }
[Name("list_price")]
public int StorePriceWithVAT { get; set; }
[Name("online_price")]
public int OnlinePriceWithVAT { get; set; }
[Name("online_value_price")]
public int OnlineValueWihtVAT { get; set; }
}
}

View File

@ -0,0 +1,27 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace OD.WebApplication.Models
{
public class ErrorMessageResult : IActionResult
{
private readonly ErrorResult _result;
public ErrorMessageResult(ErrorResult result)
{
_result = result;
}
public async Task ExecuteResultAsync(ActionContext context)
{
var objectResult = new ObjectResult(_result.Exception)
{
StatusCode = StatusCodes.Status500InternalServerError
};
await objectResult.ExecuteResultAsync(context);
}
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace OD.WebApplication.Models
{
public class ErrorResult
{
public Exception Exception { get; set; }
}
}

View File

@ -0,0 +1,11 @@
using System.IO;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.FileProviders;
namespace OD.WebApplication.Models
{
public class HomeViewModel
{
public IFormFile FileAttach { get; set; }
}
}

View File

@ -0,0 +1,49 @@
using CsvHelper.Configuration.Attributes;
namespace OD.WebApplication.Models
{
public class OriginDataModel
{
[Name("ID")]
public string Id { get; set; }
[Name("Categorías")]
public string Categories { get; set; }
[Name("Fabricante")]
public string Manufacturer { get; set; }
[Name("Precio tiendas sin IVA")]
public decimal PriceListingWithoutVAT { get; set; }
[Name("PVP sin IVA")]
public decimal PricePublicWithoutVAT { get; set; }
[Name("PVR sin IVA")]
public decimal PriceRecommendedWithoutVAT { get; set; }
[Name("Stock")]
public decimal Stock { get; set; }
[Name("Volumen")]
public float Volume { get; set; }
[Name("Nombre")]
public string Name { get; set; }
[Name("Descripción larga")]
public string LongDesc { get; set; }
[Name("Descripción breve")]
public string ShortDesc { get; set; }
[Name("Imagen")]
public string ImageUrls { get; set; }
[Name("EAN13")]
public string Ean13 { get; set; }
[Name("SDA")]
public string Sda { get; set; }
}
}

View File

@ -5,10 +5,9 @@
</PropertyGroup>
<ItemGroup>
<Content Update="Views\Home\Upload.cshtml">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<PackageReference Include="AutoMapper" Version="10.0.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.0.0" />
<PackageReference Include="CsvHelper" Version="15.0.5" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,53 @@
using System.Linq;
using AutoMapper;
using AutoMapper.Internal;
using Microsoft.AspNetCore.Builder;
using OD.WebApplication.Context;
using OD.WebApplication.Models;
namespace OD.WebApplication.Profiles
{
public class OriginToDestinationProfile : Profile
{
public OriginToDestinationProfile()
{
CreateMap<OriginDataModel, DestinationDataModel>()
.ForMember(dest => dest.Category, opt => opt.MapFrom(src=> ResolveCategory(src)))
.ForMember(dest => dest.Brand, opt => opt.MapFrom(src => src.Manufacturer))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.AlternateCode, opt => opt.MapFrom(src => ResolveAlternateCode(src)))
.ForMember(dest => dest.StorePriceWithVAT, opt => opt.MapFrom(src => ResolveAddVAT(src.PriceListingWithoutVAT)))
.ForMember(dest => dest.OnlinePriceWithVAT, opt => opt.MapFrom(src => ResolveAddVAT(src.PricePublicWithoutVAT)))
.ForMember(dest => dest.OnlineValueWihtVAT, opt => opt.MapFrom(src => ResolveAddVAT(src.PriceRecommendedWithoutVAT)));
}
private static string ResolveCategory(OriginDataModel src)
{
var categories = src.Categories.Split(',').ToList();
categories = categories.Select(del => del.Trim()).ToList();
foreach (var category in categories)
{
if (ApplicationContextHelper.BusinessApplicationCategories.Any(x =>
x.Contains(category) || category.Contains(x)))
{
return ApplicationContextHelper.BusinessApplicationCategories.First(x =>
x.Contains(category) || category.Contains(x));
}
}
return "OTRO";
}
private static string ResolveAlternateCode(OriginDataModel src)
{
return src.ShortDesc.ToUpper().Replace(" ", "");
}
private static int ResolveAddVAT(decimal input)
{
const decimal vat = 0.21m;
var result = decimal.Round(input * vat + input);
return decimal.ToInt32(result);
}
}
}

View File

@ -8,6 +8,9 @@ using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AutoMapper;
using OD.WebApplication.Context;
using OD.WebApplication.Profiles;
namespace OD.WebApplication
{
@ -24,6 +27,7 @@ namespace OD.WebApplication
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAutoMapper(typeof(Startup));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

View File

@ -1,8 +1,21 @@
@{
ViewData["Title"] = "Home Page";
@using Microsoft.AspNetCore.Mvc.Rendering
@model HomeViewModel
@{
ViewData["Title"] = "Upload page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
<h4>Escoge el CSV a parsear</h4>
<br/>
@using (Html.BeginForm("UploadCSV", "Home", FormMethod.Post, new {id = "loginForm", enctype = "multipart/form-data"}))
{
@Html.AntiForgeryToken()
<div class="form-group">
<label asp-for="@Model.FileAttach"></label>
<input asp-for="@Model.FileAttach" type="file">
<span asp-validation-for="@Model.FileAttach"></span>
</div>
<input class="btn btn-outline-primary" type="submit"/>
}
</div>

View File

@ -1,6 +0,0 @@
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>

View File

@ -1,18 +0,0 @@
@using Microsoft.AspNetCore.Mvc.Rendering
@{
ViewData["Title"] = "Upload page";
}
<div class="text-center">
<h4>Escoge el CSV a parsear</h4>
<br/>
@using (Html.BeginForm("UploadCSV", "Home", FormMethod.Post, new {id = "loginForm"}))
{
@Html.AntiForgeryToken()
<div class="form-group">
Do your magic pablo
</div>
<input class="btn btn-outline-primary" type="submit"/>
}
</div>

View File

@ -21,12 +21,6 @@
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Upload">Upload</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
@ -40,7 +34,7 @@
<footer class="border-top footer text-muted">
<div class="container">
&copy; 2020 - OD.WebApplication - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
&copy; 2020 - SI
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>