Files
2026-04-27 10:50:07 -05:00

262 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.Configuration.Attributes;
using LCU.Api.Internal.FileOperations.Validations;
using LCU.Api.Internal.FileOperations.Models;
using LCU.Library.Internal.FileOperationsModels.Models;
using LCU.Library.Internal.FileOperationsModels.Shared;
using LCU.Library.Internal.LcuEvaluation.Services.EvaluationServices;
using LCU.Library.Internal.LCUServiceFactory.Configurations;
using LCU.Library.Internal.TypeExtentions.EnumExtensions;
using Newtonsoft.Json;
using Serilog;
namespace LCU.Api.Internal.FileOperations.Services.FileActionServices;
public class ExtractIndexFileDataService : FileActionService, IFileActionService, IActionService<ExtractIndexFileDataRequest>
{
private readonly IRunRuleService _runRuleService;
private const string RegexMatch = "(\")(.*?)(\")";
public ExtractIndexFileDataService(IRunRuleService runRuleService, ServicesFactoryConfiguration servicesFactoryConfiguration)
: base(servicesFactoryConfiguration)
{
_runRuleService = runRuleService;
}
public RequestResponse ExecuteAction(QueueRequest queueRequest)
{
var requestResponse = new RequestResponse();
try
{
var request = JsonConvert.DeserializeObject<ExtractIndexFileDataRequest>(queueRequest.Request);
var requests = DynamicNameBuilder.BuildListOfRequest(request);
if (requests.Count > 1)
{
requestResponse.MessageStack.Add("More than one file was found.");
requestResponse.IsSuccessful = false;
}
else
{
requestResponse = PerformRequest(requests.FirstOrDefault());
}
requestResponse.ReturnResponse = requests.FirstOrDefault();
return requestResponse;
}
catch (Exception exception)
{
return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse,
queueRequest.FileOperationType,
exception.Message);
}
}
public RequestResponse PerformRequest(ExtractIndexFileDataRequest request)
{
return OperationService.PerformOperation(request, IsRequestValid, ExtractIndexFileData);
}
public RequestResponse PreQueueValidation(ExtractIndexFileDataRequest originalRequest)
{
var requestResponse = new RequestResponse();
return requestResponse;
}
public RequestResponse IsRequestValid(ExtractIndexFileDataRequest request, RequestResponse requestResponse)
{
requestResponse = FileValidation.ValidateFileExists(request.FinalIndexFilePath, requestResponse);
requestResponse = DirectoryValidation.ValidateDirectoryExists(request.FinalDestinationPath, requestResponse);
return requestResponse;
}
private void ExtractIndexFileData(ExtractIndexFileDataRequest request)
{
try
{
var delimiter = request.DelimiterType.GetNameFromValue();
var fileLines = GetFileContents(request.FinalIndexFilePath);
if (fileLines is null || !fileLines.Any())
{
Log.Information($"No content was extracted from filepath: ({request.FinalIndexFilePath})");
return;
}
var recordsAreWrappedInQuotes = HasQuotes(fileLines);
var headers = request.IndexHasHeaders ? GetHeaders(fileLines, delimiter, recordsAreWrappedInQuotes) : new List<string>();
var columnMax = GetColumnCount(fileLines, delimiter, recordsAreWrappedInQuotes);
var fileContent = string.Join(Environment.NewLine, fileLines);
var indexEvaluationRequests = GetCsvRecords(fileContent,
delimiter,
request.IndexHasHeaders);
var evaluatedRequests = BuildAndRunRules(indexEvaluationRequests, request);
var passedList = FilterColumns(evaluatedRequests.Passed,
request.IndexesToExtract,
columnMax,
request.ExtractAllIndexes);
var failedList = FilterColumns(evaluatedRequests.Failed,
request.IndexesToExtract,
columnMax,
true);
if (passedList is null || !passedList.Any())
{
Log.Information("No results passed evaluations to write to destination file.");
return;
}
OutputResults(request, failedList, passedList, headers, recordsAreWrappedInQuotes);
}
catch (Exception exception)
{
throw new Exception(@$"Exception thrown while trying to ExecuteRequestAction.
ExceptionMessage: {exception.Message}
Request: {JsonConvert.SerializeObject(request)}");
}
}
private (List<IndexEvaluationRequest> Passed, List<IndexEvaluationRequest> Failed) BuildAndRunRules(List<IndexEvaluationRequest> indexEvaluationRequests, ExtractIndexFileDataRequest request)
{
if (request.EvaluationRule is null)
{
return (indexEvaluationRequests, new List<IndexEvaluationRequest>());
}
var passedRules = new List<IndexEvaluationRequest>();
var failedRules = new List<IndexEvaluationRequest>();
foreach (var indexEvaluationRequest in indexEvaluationRequests)
{
if (_runRuleService.BuildAndRunRule(new List<IndexEvaluationRequest> { indexEvaluationRequest }, request.EvaluationRule))
{
passedRules.Add(indexEvaluationRequest);
}
else
{
failedRules.Add(indexEvaluationRequest);
}
}
return (passedRules, failedRules);
}
private static List<string> GetFileContents(string filepath)
=> File.ReadAllLines(filepath).Where(line => !string.IsNullOrWhiteSpace(line)).ToList();
private static bool HasQuotes(List<string> content)
=> Regex.Split(content.First(), RegexMatch).Length > 1;
private static List<string> GetHeaders(List<string> content, string delimiter, bool recordsAreWrappedInQuotes)
=> recordsAreWrappedInQuotes ? Regex.Split(content.First(), RegexMatch).Where(x => !new List<string> { delimiter, "\"" }.Contains(x)
&& !string.IsNullOrWhiteSpace(x)).ToList()
: content.First().Split(delimiter).ToList();
private static int GetColumnCount(List<string> content, string delimiter, bool recordsAreWrappedInQuotes)
{
var line = content.First();
if (!recordsAreWrappedInQuotes)
{
return line.Split(delimiter).Length;
}
var filterList = new List<string> { string.Empty, "\"", delimiter };
return Regex.Split(line, RegexMatch).Where(x => !filterList.Contains(x)).ToList().Count;
}
private static List<IndexEvaluationRequest> GetCsvRecords(string fileContent, string delimiter, bool hasHeaders)
{
using var stringReader = new StringReader(fileContent);
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = delimiter,
MissingFieldFound = null,
HasHeaderRecord = hasHeaders,
BadDataFound = null
};
using var csvReader = new CsvReader(stringReader, config);
return csvReader.GetRecords<IndexEvaluationRequest>().ToList();
}
private static List<List<string>> FilterColumns(List<IndexEvaluationRequest> records,
List<int> indexesToExtract,
int columnMax,
bool extractAllIndexes)
{
if (!extractAllIndexes && (indexesToExtract is null || !indexesToExtract.Any()))
{
throw new Exception("IndexesToExtract was empty");
}
var keepProperties = new List<string>();
var removeProperties = new List<string>();
var indexes = typeof(IndexEvaluationRequest).GetProperties().ToDictionary(
prop => ((IndexAttribute)prop.GetCustomAttributes(true).FirstOrDefault(attr => attr.GetType() == typeof(IndexAttribute)))?.Index ?? int.MaxValue,
prop => prop.Name);
var recordsDictionary = JsonConvert.DeserializeObject<List<Dictionary<string,string>>>(JsonConvert.SerializeObject(records));
keepProperties.AddRange(indexes.Where(index => extractAllIndexes ? index.Key < columnMax : indexesToExtract.Contains(index.Key))
.Select(x => x.Value).Distinct());
return recordsDictionary.Select(recordList =>
recordList.Where(record =>
(!keepProperties.Any() || keepProperties.Contains(record.Key))
&& !removeProperties.Contains(record.Key)
).Select(record => record.Value).ToList()
).ToList();
}
private static string TransformToOutputString(IEnumerable<List<string>> content, string delimiter, string wrapText)
=> string.Join(Environment.NewLine, content.Select(x => string.Join(delimiter, x.Select(y => $"{wrapText}{y}{wrapText}"))));
private static void OutputResults(ExtractIndexFileDataRequest request,
IEnumerable<List<string>> originalContent,
IEnumerable<List<string>> newContent,
List<string> headers,
bool recordIsWrappedInQuotes)
{
var delimiter = request.DelimiterType.GetNameFromValue();
var wrapText = recordIsWrappedInQuotes ? "\"" : string.Empty;
var destinationFileExists = File.Exists(request.FinalDestinationIndexFilePath);
if (request.IndexHasHeaders)
{
originalContent = originalContent.Prepend(headers);
if (!request.AppendToDestinationIndexFile || !destinationFileExists)
{
newContent = !request.ExtractAllIndexes && request.IndexesToExtract.Count > 0
? newContent.Prepend(headers.Where((_, i) => request.IndexesToExtract.Contains(i)).ToList())
: newContent.Prepend(headers);
}
}
var originalFile = TransformToOutputString(originalContent, delimiter, wrapText);
var newFile = TransformToOutputString(newContent, delimiter, wrapText);
if (request.AppendToDestinationIndexFile && destinationFileExists)
{
File.AppendAllText(request.FinalDestinationIndexFilePath, Environment.NewLine + newFile);
}
else
{
File.WriteAllText(request.FinalDestinationIndexFilePath, newFile);
}
File.WriteAllText(request.FinalIndexFilePath, originalFile);
}
}