< Summary

Information
Class: MRA.Services.Backup.Import.ImportService
Assembly: MRA.Services
File(s): D:\a\MiguelRomerART\MiguelRomerART\MRA.Services\Backup\Import\ImportService.cs
Line coverage
7%
Covered lines: 12
Uncovered lines: 146
Coverable lines: 158
Total lines: 334
Line coverage: 7.5%
Branch coverage
0%
Covered branches: 0
Total branches: 72
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ImportDrawings()0%1482380%
ReadDrawingFromRow(...)0%2040%
SetPropertyValue(...)0%1640400%

File(s)

D:\a\MiguelRomerART\MiguelRomerART\MRA.Services\Backup\Import\ImportService.cs

#LineLine coverage
 1using Microsoft.Extensions.Logging;
 2using MRA.DTO;
 3using MRA.DTO.Models;
 4using MRA.Extensions;
 5using MRA.Infrastructure.Enums;
 6using MRA.Infrastructure.Excel.Attributes;
 7using MRA.Infrastructure.Settings;
 8using MRA.Services.Excel;
 9using MRA.Services.Excel.Interfaces;
 10using MRA.Services.Models.Drawings;
 11using MRA.Services.UserInput;
 12using OfficeOpenXml;
 13using System.ComponentModel;
 14using System.Reflection;
 15
 16namespace MRA.Services.Backup.Import;
 17
 18public class ImportService : IImportService
 19{
 20    private readonly IExcelService excelService;
 21    private readonly IDrawingService drawingService;
 22    private readonly IUserInputService inputService;
 23    private readonly AppSettings appSettings;
 24    private readonly ILogger<ImportService> logger;
 25
 126    public ImportService(
 127        IExcelService excelService,
 128        IDrawingService drawingService,
 129        IUserInputService inputService,
 130        AppSettings appSettings,
 131        ILogger<ImportService> logger)
 32    {
 133        this.excelService = excelService;
 134        this.drawingService = drawingService;
 135        this.inputService = inputService;
 136        this.appSettings = appSettings;
 137        this.logger = logger;
 138    }
 39
 40    public async Task ImportDrawings()
 41    {
 042        logger.LogInformation("Iniciando Aplicación de Importación");
 43
 044        logger.LogInformation("Recuperando documentos");
 045        var listDrawings = await drawingService.GetAllDrawingsAsync(onlyIfVisible: false);
 46
 047        var filePath = inputService.ReadStringValue("Ruta del Fichero a Procesar");
 048        logger.LogInformation("Recuperando documentos desde Excel '{FilePath}'", filePath);
 49
 050        var fileInfo = new FileInfo(filePath);
 051        var listDrawingsProcessed = new List<DrawingModel>();
 052        var listDrawingsSaved = new List<DrawingModel>();
 053        var listDrawingsError = new Dictionary<int, DrawingModel>();
 54
 055        logger.LogInformation("Leyendo comandos automáticos");
 056        bool updateEverythingFromExcel = appSettings.Commands.UpdateEverythingFromExcel;
 057        if (updateEverythingFromExcel)
 58        {
 059            logger.LogWarning("Se sobreescribirán todos los cambios en Firestore con los datos del Excel");
 60        }
 61        else
 62        {
 063            logger.LogInformation("Se preguntará al usuario en caso de cambios");
 64        }
 65
 066        using (var package = new ExcelPackage(fileInfo))
 67        {
 068            logger.LogInformation("Leyendo hoja principal '{Sheet}'", ExcelService.EXCEL_DRAWING_SHEET_NAME);
 069            var workSheet = package.Workbook.Worksheets[ExcelService.EXCEL_DRAWING_SHEET_NAME];
 070            if (workSheet == null)
 71            {
 072                throw new Exception($"Worksheet '{ExcelService.EXCEL_DRAWING_SHEET_NAME}' not found in the file.");
 73            }
 74
 075            logger.LogInformation("Obteniendo propiedades del DTO de Drawing");
 076            var drawingProperties = excelService.GetPropertiesAttributes<DrawingModel>();
 77
 078            logger.LogInformation("Obteniendo mapeo entre nombres y números de columnas en el Excel");
 079            Dictionary<string, int> nameToColumnMap = excelService.GetColumnMapDrawing(workSheet);
 80
 081            int row = 2;
 082            while (workSheet.Cells[row, 1].Value != null)
 83            {
 084                bool error = false;
 085                logger.LogInformation("Leyendo fila {Row}", row);
 086                DrawingModel drawingExcel = ReadDrawingFromRow(workSheet, drawingProperties, nameToColumnMap, row);
 87
 088                logger.LogInformation("Dibujo '{Id}' leído desde Excel", drawingExcel.Id);
 089                drawingExcel.Date = drawingExcel.DateObject.FormattedDate();
 90
 091                DrawingModel? drawingDatabase = listDrawings.FirstOrDefault(x => x.Id == drawingExcel.Id);
 92
 093                if (drawingDatabase != null)
 94                {
 95                    try
 96                    {
 097                        bool differentValues = false;
 098                        logger.LogInformation("Existe un dibujo '{Id}'. Procediendo a EDITAR", drawingDatabase.Id);
 99
 0100                        foreach (var prop in drawingProperties.Where(x => x.Property.CanWrite))
 101                        {
 0102                            if (!prop.Attribute.IgnoreOnImport && !prop.SameValues(drawingExcel, drawingDatabase))
 103                            {
 104                                bool updateValue = false;
 0105                                logger.LogInformation("-------------------------");
 0106                                logger.LogWarning("'{Id}' tiene diferentes valores para '{Property}'.", drawingExcel.Id,
 107
 0108                                logger.LogError("En BBDD: {Value}", prop.GetValueToPrint(drawingDatabase));
 0109                                logger.LogWarning("En EXCEL: {Value}", prop.GetValueToPrint(drawingExcel));
 110
 0111                                updateValue = updateEverythingFromExcel || inputService.ReadBoolValue($"Actualizar valor
 112
 0113                                if (updateValue)
 114                                {
 0115                                    differentValues = true;
 0116                                    logger.LogInformation("Actualizando valor con EXCEL: {Value}", prop.GetValueToPrint(
 0117                                    prop.Property.SetValue(drawingDatabase, prop.GetValue(drawingExcel));
 118                                }
 119                                else
 120                                {
 0121                                    logger.LogInformation("Permanece el valor de FIRESTORE: {Value}", prop.GetValueToPri
 122                                }
 123                            }
 124                        }
 125
 0126                        if (differentValues)
 127                        {
 0128                            logger.LogInformation("Actualizando '{Id}' en BBDD", drawingDatabase.Id);
 0129                            var updatedDrawingSaved = await drawingService.SaveDrawingAsync(drawingDatabase);
 0130                            logger.LogInformation("'{Id}' guardado con éxito", updatedDrawingSaved.Id);
 131
 0132                            if (!listDrawingsSaved.Any(x => x.Id == updatedDrawingSaved.Id))
 133                            {
 0134                                listDrawingsSaved.Add(updatedDrawingSaved);
 135                            }
 0136                        }
 137                        else
 138                        {
 0139                            logger.LogInformation("No se han detectado cambios en '{Id}' respecto al Excel. Se ignora", 
 140                        }
 0141                    }
 0142                    catch (Exception ex)
 143                    {
 0144                        logger.LogError(ex, "No se pudo actualizar el dibujo '{Id}'.", drawingExcel.Id);
 0145                        error = true;
 0146                    }
 147                }
 148                else
 149                {
 150                    try
 151                    {
 0152                        logger.LogWarning("No existe ningún dibujo con ID '{Id}'", drawingExcel.Id);
 0153                        var newDrawingSaved = await drawingService.SaveDrawingAsync(drawingExcel);
 0154                        logger.LogInformation("Dibujo '{Id}' creado con éxito", newDrawingSaved.Id);
 155
 0156                        if (!listDrawingsSaved.Any(x => x.Id == newDrawingSaved.Id))
 157                        {
 0158                            listDrawingsSaved.Add(newDrawingSaved);
 159                        }
 0160                    }
 0161                    catch (Exception ex)
 162                    {
 0163                        logger.LogError(ex, "No se pudo crear el dibujo '{Id}'.", drawingExcel.Id);
 0164                        error = true;
 0165                    }
 166                }
 167
 0168                if (!error && !listDrawingsProcessed.Any(x => x.Id == drawingExcel.Id))
 169                {
 0170                    listDrawingsProcessed.Add(drawingExcel);
 171                }
 172
 0173                if (error)
 174                {
 0175                    listDrawingsError.Add(row, drawingExcel);
 176                }
 177
 0178                if (!updateEverythingFromExcel)
 179                {
 0180                    logger.LogInformation("Pulsa cualquier tecla para continuar con la siguiente línea. Actual: {Row}.",
 0181                    inputService.ReadKey();
 182                }
 183
 0184                row++;
 0185            }
 0186        }
 187
 0188        logger.LogInformation("Dibujos En Firestore: {Count}.", listDrawings.Count());
 0189        logger.LogInformation("Dibujos Procesados: {Count}.", listDrawingsProcessed.Count);
 0190        logger.LogInformation("Guardados: {Count}.", listDrawingsSaved.Count);
 0191        logger.LogError("Errores: {Count}.", listDrawingsError.Count);
 0192        if (listDrawingsError.Any())
 193        {
 0194            foreach (var error in listDrawingsError)
 195            {
 0196                logger.LogError("Fila {Key}, '{Id}'", error.Key, error.Value.Id);
 197            }
 198        }
 0199    }
 200
 201    private static DrawingModel ReadDrawingFromRow(ExcelWorksheet workSheet, List<ExcelColumnInfo> drawingProperties, Di
 202    {
 0203        var drawing = new DrawingModel();
 204
 0205        foreach (var propInfo in drawingProperties.Where(x => x.Property.CanWrite))
 206        {
 0207            if (!nameToColumnMap.TryGetValue(propInfo.Attribute.Name, out int col))
 208            {
 209                continue;
 210            }
 211
 212            //throw new NotImplementedException("TODO: fix reading value null in boolean and numbers.");
 213
 0214            var cellValue = workSheet.GetValue(row, col);
 0215            SetPropertyValue(drawing, propInfo.Property, cellValue);
 216        }
 217
 0218        return drawing;
 219    }
 220
 221    private static void SetPropertyValue(DrawingModel drawing, PropertyInfo property, object cellValue)
 222    {
 0223        if (cellValue == null)
 224        {
 0225            property.SetValue(drawing, null);
 0226            return;
 227        }
 228
 0229        Type propType = property.PropertyType;
 230
 231        try
 232        {
 0233            if (propType == typeof(string))
 234            {
 0235                property.SetValue(drawing, cellValue.ToString());
 236            }
 0237            else if (propType == typeof(int))
 238            {
 0239                property.SetValue(drawing, Convert.ToInt32(cellValue));
 240            }
 0241            else if (propType == typeof(long))
 242            {
 0243                property.SetValue(drawing, Convert.ToInt64(cellValue));
 244            }
 0245            else if (propType == typeof(double))
 246            {
 0247                property.SetValue(drawing, Convert.ToDouble(cellValue));
 248            }
 0249            else if (propType == typeof(decimal))
 250            {
 0251                property.SetValue(drawing, Convert.ToDecimal(cellValue));
 252            }
 0253            else if (propType == typeof(DateTime))
 254            {
 0255                property.SetValue(drawing, DateTime.FromOADate(Convert.ToDouble(cellValue)));
 256            }
 0257            else if (propType == typeof(bool))
 258            {
 0259                switch (property.Name)
 260                {
 261                    case "Visible":
 0262                        property.SetValue(drawing, cellValue.ToString() == ExcelService.EXCEL_VISIBLE_VALUE);
 0263                        break;
 264                    case "Favorite":
 0265                        property.SetValue(drawing, cellValue.ToString() == ExcelService.EXCEL_FAVORITE_VALUE);
 0266                        break;
 267                    default:
 0268                        property.SetValue(drawing, cellValue.ToString() == "TRUE");
 0269                        break;
 270                }
 271            }
 0272            else if (propType == typeof(IEnumerable<string>))
 273            {
 274                List<string> list;
 0275                switch (property.Name)
 276                {
 277                    case "Tags":
 0278                        list = cellValue.ToString().Split(new[] { DrawingTagManager.TAG_SEPARATOR }, StringSplitOptions.
 0279                        break;
 280                    default:
 0281                        list = cellValue.ToString().Split(new[] { ExcelService.EXCEL_SEPARATOR_COMMENTS }, StringSplitOp
 282                        break;
 283                }
 284
 0285                property.SetValue(drawing, list);
 286            }
 0287            else if (propType == typeof(Uri))
 288            {
 0289                if (Uri.IsWellFormedUriString(cellValue.ToString(), UriKind.Absolute))
 290                {
 0291                    property.SetValue(drawing, new Uri(cellValue.ToString()));
 292                }
 293            }
 0294            else if (propType.IsEnum) // Manejo de enumeraciones
 295            {
 0296                string stringValue = cellValue.ToString();
 297
 298                // Intentar mapear al enum por nombre
 0299                if (Enum.TryParse(propType, stringValue, true, out var enumValue))
 300                {
 0301                    property.SetValue(drawing, enumValue);
 0302                    return;
 303                }
 304
 305                // Intentar mapear al enum por descripción
 0306                foreach (var field in propType.GetFields())
 307                {
 0308                    var descriptionAttribute = field.GetCustomAttribute<DescriptionAttribute>();
 0309                    if (descriptionAttribute != null && descriptionAttribute.Description.Equals(stringValue, StringCompa
 310                    {
 0311                        var enumParsedValue = Enum.Parse(propType, field.Name);
 0312                        property.SetValue(drawing, enumParsedValue);
 0313                        return;
 314                    }
 315                }
 316
 317                // Si no se puede mapear, asignar valor predeterminado (si existe)
 0318                var defaultEnumValueAttribute = propType.GetCustomAttribute<DefaultEnumValueAttribute>();
 0319                if (defaultEnumValueAttribute != null)
 320                {
 0321                    property.SetValue(drawing, defaultEnumValueAttribute.DefaultValue);
 322                }
 323                else
 324                {
 0325                    property.SetValue(drawing, Activator.CreateInstance(propType));
 326                }
 327            }
 0328        }
 0329        catch (Exception ex)
 330        {
 0331            Console.WriteLine($"Error setting property {property.Name}: {ex.Message}");
 0332        }
 0333    }
 334}