Gremlin/Gremlin_BlazorServer/Data/DBClasses/GenericImporter.cs

986 lines
43 KiB
C#

using System.Diagnostics;
using System.Globalization;
using System.Text;
using Gremlin_BlazorServer.Data.EntityClasses;
using Gremlin_BlazorServer.Services;
using Gremlin_BlazorServer.Utilities.GUClasses;
using Microsoft.VisualBasic.FileIO;
using static Gremlin_BlazorServer.Data.EntityClasses.Enums;
namespace Gremlin_BlazorServer.Data.DBClasses;
public class GenericImporter {
//Private members
private static readonly DateTime farInTheFuture = DateTime.Parse("2050-12-31t00:00:00.000000z", CultureInfo.CurrentCulture);
private static Encoding encoding = Encoding.UTF8;
private static TextFieldParser csvParser = new(Filepath, encoding, true);
private static readonly GremlinDb gremlinDb = new();
//Public properties
public static string? Filepath { get; set; }
private static string[] Separators {
get => csvParser.Delimiters!;
set {
if (value.Length > 0) csvParser.SetDelimiters(value);
}
}
public static bool DataHasHeadings { get; set; }
private static Encoding Encoding {
set {
encoding = value;
ResetParser();
}
}
////Constructors
//public GenericImporter(string filepath)
//{
// Filepath = filepath;
// _dataHasHeadings = true;
// _encoding = FileIO.GetEncoding(Filepath);
// TextFieldParser _csvParser = new(Filepath, _encoding);
// _csvParser.SetDelimiters(Separators);
// _csvParser.HasFieldsEnclosedInQuotes = true;
//}
//public GenericImporter(string filepath, Encoding encoding)
//{
// Filepath = filepath;
// _dataHasHeadings = true;
// _encoding = encoding;
// TextFieldParser _csvParser = new(Filepath, _encoding);
// _csvParser.SetDelimiters(Separators);
// _csvParser.HasFieldsEnclosedInQuotes = true;
//}
//public GenericImporter(string filepath, string[] separators)
//{
// Filepath = filepath;
// _dataHasHeadings = true;
// _encoding = FileIO.GetEncoding(Filepath);
// TextFieldParser _csvParser = new(Filepath, _encoding);
// Separators = separators;
// _csvParser.HasFieldsEnclosedInQuotes = true;
//}
//public GenericImporter(string filepath, Encoding encoding, string[] separators)
//{
// Filepath = filepath;
// _encoding = encoding;
// _dataHasHeadings = true;
// TextFieldParser _csvParser = new(Filepath, _encoding);
// Separators = separators;
// _csvParser.HasFieldsEnclosedInQuotes = true;
//}
//Public methods
private static void SetFilepath() {
//Filepath = FileIO.GetFilepathFromUser();
}
private static void ResetParser() {
if (Filepath != "") {
TextFieldParser newParser = new(Filepath, encoding);
newParser.SetDelimiters(Separators);
newParser.HasFieldsEnclosedInQuotes = true;
csvParser = newParser;
}
}
public static bool Run(string filepath, string separator, string encoding) {
Filepath = filepath.Replace(@"\\", @"\");
try {
Encoding = Encoding.GetEncoding(encoding);
}
catch (Exception) {
Encoding = FileService.GetEncoding(Filepath);
}
Separators = new[] { separator };
return ImportFile();
}
public async Task<bool> RunAsync(string filepath, string separator, string encoding) {
if (filepath is "" or null) return false;
Filepath = filepath.Replace(@"\\", @"\");
try {
Encoding = Encoding.GetEncoding(encoding);
}
catch (Exception) {
Encoding = FileService.GetEncoding(Filepath);
}
Separators = new[] { separator };
return await Task.Run(ImportFile);
}
public static string GuessSeparator(string filepath) //, string encoding)
{
if (filepath is "" or null) return string.Empty;
string[] candidates = { "|", ",", ";" };
int numberOfCandidates = candidates.Length;
const int numberOfLinesToEvaluate = 100;
int[,] score = new int[numberOfLinesToEvaluate, numberOfCandidates];
Filepath = filepath.Replace(@"\\", @"\");
//if (csvParser == null)
//{
// try
// {
// Encoding = Encoding.GetEncoding(encoding);
// }
// catch (Exception)
// {
// Encoding = FileIO.GetEncoding(GenericImporter.filepath);
// }
//}
using (csvParser) {
for (int i = 0; i < numberOfLinesToEvaluate; i++)
if (!csvParser.EndOfData) {
string line = csvParser.ReadLine()!;
for (int j = 0; j < numberOfCandidates; j++) score[i, j] = line.Split(candidates[j]).Length;
}
}
List<(string, int, float)> scoreBoard = new(); //Item1 = Separator, Item2 = Score (Anzahl aufeinanderfolgender Zeilen mit gleicher Anzahl von Fields), Item3 = Count (durchschnittliche Anzahl von Fields in Zeile)
for (int j = 0; j < numberOfCandidates; j++) {
int x = 0;
float average = 0;
for (int i = 0; i < numberOfLinesToEvaluate - 1; i++) {
if (score[i, j] == score[i + 1, j] && score[i, j] > 1) x++;
average += score[i, j];
}
average += score[numberOfLinesToEvaluate - 1, j];
average /= numberOfLinesToEvaluate;
scoreBoard.Add((candidates[j], x, average));
}
ResetParser();
return scoreBoard.Find(f => f.Item2 == scoreBoard.Max(x => x.Item2) && f.Item3 == scoreBoard.Max(x => x.Item3)).Item1;
}
private static bool ImportFile()
//Ein (möglichst) generischer Importer
//1. Dateipfad erfassen
//2. Column <-> Property Mapping
//3. Typ der zu importierenden Daten herausfinden
//4. Daten einlesen, konvertieren, validieren, Metadaten setzen und alles in Liste(n) speichern
//5. Datenliste(n) in DB speichern
{
if (Filepath == "") SetFilepath();
if (Filepath == "") return false;
using (csvParser) {
//für geneerischen Code:
//int numberOfLines = File.ReadAllLines(filepath).Length;
//Assembly Gremlin = Assembly.GetExecutingAssembly();
//dynamische Spaltenzuordnung in Dictonary speichern
string[] fields = csvParser.ReadFields()!;
Dictionary<string, string> mappingDictionary = ReadMappingDictionaryFromFile();
Dictionary<string, int> mappingTable = MapDataHeading(fields, mappingDictionary);
//determine data type to be imported
DataIdentificator dataIdentificator = new(mappingTable);
List<string> detectedDataTypes = dataIdentificator.Identify();
//check for detectedDataTypes = empty (possible when DataIdentificator.Identify(MustMatchAllQualifer = true) and no dataset matches all qualifiers of a type)
if (detectedDataTypes.Count == 0) {
Console.WriteLine($"DataIdentificator.Identify() konnte die Datenart nicht bestimmen!{Environment.NewLine}Versuchen Sie den Import mit einer dezidierten Importerfunktion.");
return false;
}
return detectedDataTypes[0] switch {
"ProductLine" => ImportProductLine(csvParser, mappingTable),
"AccountType" => ImportAccountType(csvParser, mappingTable),
"SubMarket" => ImportSubMarket(csvParser, mappingTable),
"Account" => ImportAccounts(mappingTable),
"Contact" => ImportContacts(mappingTable),
"LSAG" => ImportLsag(mappingTable),
"Product" => ImportProducts(mappingTable),
"CustomDescription" => ImportCustomDescriptions(), // mappingTable);
_ => false
};
}
}
public static bool ImportFile(string filepath) {
Filepath = filepath;
return ImportFile();
}
private static bool ImportCustomDescriptions() //Dictionary<string, int> mappingTable)
{
List<CustomDescription> cDsReadFromFile = new(2500);
Encoding = Encoding.GetEncoding("UTF-8"); //Custom-Descriptions-CSV hat festes Encoding.
using (csvParser) {
// Skip the row with the column names:
_ = csvParser.ReadLine();
while (!csvParser.EndOfData) {
// Read current line fields, pointer moves to the next line.
string[] fields = csvParser.ReadFields()!;
CustomDescription importedCd = new() {
ProductNumber = fields[0],
OptionNumber = fields[1],
Heading = fields[3],
DescriptionText = fields[4],
CoverletterText = fields[5],
Notes = fields[6],
DataModificationByUser = "Importer",
DataStatus = Status.Active.ToString(),
DataValidUntil = farInTheFuture,
DataVersionComment = "Initial Importer by CD-ImporterFomCsv",
// Product = new Product(),
Supplier = new() { AccountName = fields[2] is "" or "RB" ? "Agilent Technologies" : fields[2] }
};
importedCd.DataCreationDate = importedCd.DataValidFrom = importedCd.DataModificationDate = DateTime.Now;
importedCd.DataVersionNumber = 1;
cDsReadFromFile.Add(importedCd);
}
//Eingelesenen Custum Desciptions in DB schreiben:
//Check for 3PPs that not yet in the db.
//Step 1a: Add 3PP-Products from CD-List to list
//Step 1b: Add list to db.
//Step 2: Add CDs to products.
using (GremlinDb gremlinDb = new()) {
//Step 1a
List<Product> thirdPartyProductsFromImportedCDs = new();
foreach (CustomDescription cd in cDsReadFromFile)
if (cd.Supplier.AccountName != "Agilent Technologies") {
Product new3PpProduct = new() {
CustomDescription = cd,
HasBreakPrices = false,
ListPrice = 0,
ProductNumber = cd.ProductNumber,
OptionNumber = cd.OptionNumber,
ProductStatus = Status.Active.ToString(),
SapLongDescription = "",
SapShortDescription = "",
Weight = 0,
IntroductionDate = DateTime.Now.Date,
BreakRangeFrom = 0,
BreakRangeTo = 0,
ProductLine = DbHelper.ResolveProductLine(gremlinDb, "3P")
};
new3PpProduct.CustomDescription.Supplier = DbHelper.ResolveAccountByName(gremlinDb, new3PpProduct.CustomDescription.Supplier.AccountName);
_ = MetaDataSetter.ForImport(new3PpProduct, "GenericImporter-Method", "Created at import from Custom Descriptions.");
thirdPartyProductsFromImportedCDs.Add(new3PpProduct);
}
//Step 1b
gremlinDb.Products.AddRange(thirdPartyProductsFromImportedCDs); //Imports both the products and the associated custom descriptions!
_ = gremlinDb.SaveChanges();
// Produkt aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen.
//Step 2
List<CustomDescription> importedCDsWithEfReferences = new(100000);
List<CustomDescription> cDsWithoutEfReferences = new(100); //nur zur Kontrolle, wird nicht verwendet.
List<Product> productsInDb = gremlinDb.Products.ToList();
Account agilent = gremlinDb.Accounts.Single(a => a.AccountName == "Agilent Technologies");
foreach (CustomDescription cD in cDsReadFromFile) {
//Skip Desciptions, if it has been already imported above (as part from 3PP)
// if (thirdPartyProductsFromImportedCDs.Equals(cD.Product)) continue;
//Establish EF Reference. If no PN/Opt found, then skip this custom description.
//CD.Product = GetProduct(db, CD.ProductNumber, CD.OptionNumber); //ResolveXY-functions return null, if no match is found in db.
// cD.Product = productsInDb.Where(product => product.ProductNumber == cD.ProductNumber && product.OptionNumber == cD.OptionNumber).FirstOrDefault();
//Establish EF Reference: If no Supplier-Account found, then skip this custom description (shouldn't happen), else set Supplier to Agilent (3PP-Supplier have been processed before and their products should not be part of this list).
cD.Supplier = agilent;
//prepare properties
_ = MetaDataSetter.ForImport(cD, "GenericImporter-Method", "Initial import by CSV Importer (Function GenericImporter.ImportCustomDescriptions)");
//add to final list of CDs, that will go into the db.
importedCDsWithEfReferences.Add(cD);
}
gremlinDb.CustomDescriptions.AddRange(importedCDsWithEfReferences);
_ = gremlinDb.SaveChanges();
//Bestätigung senden
Console.WriteLine($"Es wurden {importedCDsWithEfReferences.Count} eigene Beschreibungen erfolgreich der Datenbank hinzugefügt.{Environment.NewLine}Es wurden {thirdPartyProductsFromImportedCDs.Count} 3PP-Produkte neu angelegt.");
}
return true;
}
}
private static bool ImportProducts(Dictionary<string, int> mappingTable) {
List<Product> productsReadFromFile = new(ParseProductFile(mappingTable));
return InsertProducts(productsReadFromFile);
}
private static bool InsertProducts(List<Product> products) {
using GremlinDb gremlinDb = new();
List<ProductLine> productLines = gremlinDb.ProductLines.ToList();
foreach (Product product in products) {
//var query = db.ProductLines
// .Where(a => a.ProductLineAbbreviation == product.ProductLine.ProductLineAbbreviation)
// .First();
//product.ProductLine = query;
//product.ProductLine = ResolveProductLine(db, product.ProductLine.ProductLineAbbreviation);
product.ProductLine = productLines.Find(x => x.ProductLineCode == product.ProductLine.ProductLineCode) ?? new ProductLine();
_ = MetaDataSetter.ForImport(product, "GenericImporter-Method");
}
gremlinDb.Products.AddRange(products);
_ = gremlinDb.SaveChanges();
//Bestätigung senden
Console.WriteLine($"Es wurden {products.Count} Produkte erfolgreich der Datenbank hinzugefügt.");
return true;
}
private static List<Product> ParseProductFile(Dictionary<string, int> columnNumberOf) {
List<Product> results = new(100000);
using (csvParser) {
while (!csvParser.EndOfData) {
// Read current line fields, pointer moves to the next line.
Product importedProduct = new() { ProductLine = new() };
string[] fields = csvParser.ReadFields()!;
//Kontrolle, ob Trennzeichen in Daten vorhanden ist:
if (fields.Length > 27) //27 ist der Normalfall
//Sonderfall "EnVisionTM G|2":
//suche fields[x], das auf " G" endet und auf das field[x+1] mit "2 " beginnt, kombiniere die beiden und schiebe alle darauffolgenden Felder in diesem Datensatz eins nach links.
for (int i = 0; i < fields.Length - 1; i++) //fields.Length - 1, um durch i+1 nicht aus dem Index zu laufen
if (fields[i].EndsWith(" G"))
if (fields[i + 1].StartsWith("2 ")) {
fields[i] = fields[i] + fields[i + 1];
for (int j = i + 2; j < fields.Length; j++) fields[j - 1] = fields[j];
}
//NICHT ausgewertete Felder:
//fields[0] = Position [in CPL]
//fields[8] = Warranty
//fields[10] = PH Code
//fields[11] = PH Description
//fields[15] = Country of Manufacturing
//fields[16] = ECCL
//fields[17] = M41
//fields[18] = First Supplier Code
//fields[19] = Harmonized Tarif Schedule
//fields[20] = Hazardous Good Flag
//fields[21] = Order instructions
//fields[23] = End of Production Date
//fields[24] = End of Support Date
importedProduct.ProductNumber = fields[columnNumberOf["ProductNumber"]];
if (fields[columnNumberOf["OptionNumber"]].Length == 4) //Optionsnummer mit führendem Apostroph
importedProduct.OptionNumber = fields[columnNumberOf["OptionNumber"]].Substring(1); //schneidet erstes Zeichen/Apostroph weg
else if (fields[columnNumberOf["OptionNumber"]].Length == 3) //3-stellige Optionsnummer übernehmen; keine Aktion bei leerem Feld (keine Optionsnummer)
importedProduct.OptionNumber = fields[columnNumberOf["OptionNumber"]];
importedProduct.SapShortDescription = fields[columnNumberOf["SapShortDescription"]];
importedProduct.ListPrice = decimal.Parse(fields[columnNumberOf["ListPrice"]], new CultureInfo("de-de")); //parsing! compare with old value (either from CSV or from DB) -> price change?
//if (fields[columnNumberOf["ListPrice"]] != "")
// if (decimal.Parse(fields[columnNumberOf["ListPrice"]], new CultureInfo("de-de")) != ImportedProduct.ListPrice)
// listpriceHasChanged = true;
importedProduct.BreakRangeFrom = fields[columnNumberOf["BreakRangeFrom"]] == "" ? 0 : Convert.ToInt32(fields[columnNumberOf["BreakRangeFrom"]]);
importedProduct.HasBreakPrices = importedProduct.BreakRangeFrom > 0;
importedProduct.BreakRangeTo = fields[columnNumberOf["BreakRangeTo"]] is "" or "+" ? 0 : Convert.ToInt32(fields[columnNumberOf["BreakRangeTo"]]); //erfasst sowohl Produkte ohne Break-Preise ("") als auch "+" bei Mengenangaben a la "100+" (= von 100 bis unendlich)
importedProduct.ProductLine.ProductLineCode = fields[columnNumberOf["ProductLineCode"]];
switch (fields[columnNumberOf["ProductStatus"]]) {
case "Active":
importedProduct.DataStatus = Status.Active.ToString();
importedProduct.ProductStatus = Status.Active.ToString();
break;
case "New Product" or "New":
importedProduct.DataStatus = Status.Active.ToString();
importedProduct.ProductStatus = Status.New.ToString();
break;
case "Price Changed":
importedProduct.DataStatus = Status.Active.ToString();
importedProduct.ProductStatus = Status.PriceUpdated.ToString();
break;
default:
importedProduct.DataStatus = Status.StatusUpdated.ToString();
break;
}
importedProduct.Weight = ParseWeight(fields[columnNumberOf["Weight"]]);
if (fields[columnNumberOf["WeightUnit"]] == "G") importedProduct.Weight /= 1000; //Umrechnung g in kg
if (fields[columnNumberOf["IntroductionDate"]] != "") importedProduct.IntroductionDate = DateTime.Parse(fields[columnNumberOf["IntroductionDate"]]);
importedProduct.SapLongDescription = fields[columnNumberOf["SapLongDescription"]];
results.Add(importedProduct);
}
}
return results;
}
private static float ParseWeight(string input) {
StringBuilder sb = new();
if (input.StartsWith(".")) input = sb.Append(0).Append(input).ToString();
try {
return float.Parse(input, new CultureInfo("en-us"));
}
catch (Exception ex) {
Debug.WriteLine(ex, "Unhandled Error in Function 'ParseWeight'");
return 0;
}
}
private static bool ImportLsag(Dictionary<string, int> mappingTable) {
bool result = ImportAccounts(mappingTable);
ResetParser();
_ = csvParser.ReadFields(); //Skip Heading
result = result && ImportContacts(mappingTable);
return result;
}
private static bool ImportContacts(Dictionary<string, int> columnNumberOf) {
List<Contact> contactsReadFromFile = new(8000);
using (csvParser) {
while (!csvParser.EndOfData) {
Contact importedContact = new();
string[] fields = csvParser.ReadFields()!;
//No conversion
importedContact.AcademicTitle = fields[columnNumberOf["AcademicTitle"]];
importedContact.FirstName = fields[columnNumberOf["FirstName"]];
importedContact.LastName = fields[columnNumberOf["LastName"]];
importedContact.EMail = fields[columnNumberOf["EMail"]];
importedContact.Department = fields[columnNumberOf["Department"]];
importedContact.Room = fields[columnNumberOf["Room"]];
importedContact.PhoneNumber = fields[columnNumberOf["PhoneNumber"]];
importedContact.Function = fields[columnNumberOf["Function"]];
importedContact.MobileNumber = fields[columnNumberOf["MobileNumber"]];
//Convert Gender
importedContact.Gender = fields[columnNumberOf["Gender"]] == "M" ? (byte)Gender.Male : fields[columnNumberOf["Gender"]] == "F" ? (byte)Gender.Female : (byte)Gender.Unknown;
//Convert OptIn Status
importedContact.OptInStatus = fields[columnNumberOf["OptInStatus"]] switch {
"Opt In" => true,
"Opt Out" => false,
_ => false
};
//Convert "SAP Contact Number"
try {
importedContact.SapContactNumber = Convert.ToUInt32(fields[columnNumberOf["SAPContactNumber"]], CultureInfo.CurrentCulture);
}
catch (FormatException ex) {
//errorhandling here: "Unable to parse input: " + field[0] + " as uint"
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["SAPContactNumber"]];
if (errorRaiser == "") errorRaiser = "No Contact Number in this row!";
DbHelper.DisplayErrorDetails(ex, errorRaiser);
return false;
}
catch (OverflowException ex) {
//errorhandling here: "Number cannot fit in an Uint."
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["SAPContactNumber"]];
if (errorRaiser == "") errorRaiser = "No Contact Number in this row!";
DbHelper.DisplayErrorDetails(ex, errorRaiser);
return false;
}
//Convert "Account Created on"
int year = Convert.ToInt32(fields[columnNumberOf["SAPContactCreationDate"]].Substring(0, 4), CultureInfo.CurrentCulture);
int month = Convert.ToInt32(fields[columnNumberOf["SAPContactCreationDate"]].Substring(4, 2), CultureInfo.CurrentCulture);
int day = Convert.ToInt32(fields[columnNumberOf["SAPContactCreationDate"]].Substring(6, 2), CultureInfo.CurrentCulture);
importedContact.SapContactCreationDate = new(year, month, day);
//Convert "No Phone Calls"
if (fields[columnNumberOf["NoPhoneCalls"]] == "1") importedContact.NoPhoneCalls = true;
//Convert "No Hardcopy Mailing"
if (fields[columnNumberOf["NoHardcopyMailing"]] == "1") importedContact.NoHardcopyMailing = true;
//SAPAccountID in Contact.Notes speichern, um den entsprechenden Account aus DB heraussuchen zu können:
importedContact.Notes = fields[columnNumberOf["SAPAccountNumber"]];
contactsReadFromFile.Add(importedContact);
}
//Eingelesenen Account in DB schreiben:
using (GremlinDb gremlinDb = new()) {
foreach (Contact contact in contactsReadFromFile) {
// AccountID aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen.
contact.Account = DbHelper.ResolveAccountById(gremlinDb, Convert.ToUInt32(contact.Notes));
contact.Notes = "";
_ = MetaDataSetter.ForImport(contact, "GenericImporter-Method");
}
gremlinDb.Contacts.AddRange(contactsReadFromFile);
_ = gremlinDb.SaveChanges();
}
}
//Bestätigung senden
Console.WriteLine($"Es wurden {contactsReadFromFile.Count} Contacts erfolgreich der Datenbank hinzugefügt.");
return true;
}
private static bool ImportAccounts(Dictionary<string, int> columnNumberOf) {
List<Account> accountsReadFromFile = new(1000);
while (!csvParser.EndOfData) {
bool dataHasError = false;
// Read current line fields, pointer moves to the next line.
Account importedAccount = new();
{
importedAccount.SubMarket = new();
importedAccount.AccountType = new();
importedAccount.Contacts = new List<Contact>();
}
string[] fields = csvParser.ReadFields()!;
//Konvertierung erforderlich:
try {
importedAccount.SapAccountNumber = Convert.ToUInt32(fields[columnNumberOf["SAPAccountNumber"]]);
}
catch (FormatException ex) {
//errorhandling here: "Unable to parse input: " + field[0] + " as uint"
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["SAPAccountNumber"]];
if (errorRaiser == "") errorRaiser = "No Account Number in this row!";
DbHelper.DisplayErrorDetails(ex, errorRaiser);
return false;
}
catch (OverflowException ex) {
//errorhandling here: "Number cannot fit in an Uint."
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["SAPAccountNumber"]];
if (errorRaiser == "") errorRaiser = "No Account Number in this row!";
DbHelper.DisplayErrorDetails(ex, errorRaiser);
return false;
}
try {
importedAccount.Zip = Convert.ToUInt32(fields[columnNumberOf["ZIP"]]);
}
catch (FormatException ex) {
//errorhandling here: "Unable to parse input: " + field[0] + " as uint"
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["ZIP"]];
if (errorRaiser == "") errorRaiser = "No ZIP in this row!";
DbHelper.DisplayErrorDetails(ex, errorRaiser);
return false;
}
catch (OverflowException ex) {
//errorhandling here: "Number cannot fit in an Uint."
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["ZIP"]];
if (errorRaiser == "") errorRaiser = "No ZIP in this row!";
DbHelper.DisplayErrorDetails(ex, errorRaiser);
return false;
}
//Konvertierung für AccountCreatedinSAPOn Property:
//Test auf Inhalt bzw. Eintrag in der Mapping-Table (optionales Feld)
if (columnNumberOf.TryGetValue("AccountCreatedinSAPOn", out int columnNumber))
if (fields[columnNumberOf["AccountCreatedinSAPOn"]].Length != 0) {
int year = Convert.ToInt32(fields[columnNumber].Substring(0, 4));
int month = Convert.ToInt32(fields[columnNumber].Substring(4, 2));
int day = Convert.ToInt32(fields[columnNumber].Substring(6, 2));
importedAccount.AccountCreatedInSapOn = new(year, month, day);
}
//Convert City von Großschreibung zu Normalschreibung
importedAccount.City = fields[columnNumberOf["City"]].First() + fields[columnNumberOf["City"]].Substring(1).ToLower();
//keine Konvertierung nötig:
importedAccount.AccountName = fields[columnNumberOf["AccountName"]];
importedAccount.Street = fields[columnNumberOf["Street"]];
importedAccount.SubMarket.SubMarketCode = fields[columnNumberOf["SubMarketCode"]];
importedAccount.SubMarket.DataStatus = Status.Active.ToString();
importedAccount.AccountType.AccountTypeCode = fields[columnNumberOf["AccountTypeCode"]];
importedAccount.AccountType.DataStatus = Status.Active.ToString();
importedAccount.PhoneNumber = fields[columnNumberOf["PhoneNumber"]];
importedAccount.DataStatus = Status.Active.ToString();
//Validierungen:
if (importedAccount.AccountName == "" || importedAccount.City == "" || importedAccount.Street == "" || importedAccount.SubMarket.SubMarketCode == "" || importedAccount.AccountType.AccountTypeCode == "" || importedAccount.SapAccountNumber == 0) dataHasError = true;
//Validierten Account der Liste hinzufügen:
if (dataHasError == false) _ = importedAccount.AddIfUniqueTo(accountsReadFromFile);
}
//Eingelesenen Account in DB schreiben:
foreach (Account account in accountsReadFromFile) {
// AccountType aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen.
AccountType? accountType = gremlinDb.AccountTypes.First(a => a.AccountTypeCode == account.AccountType.AccountTypeCode);
account.AccountType = accountType;
account.AccountType = DbHelper.ResolveAccountType(gremlinDb, account.AccountType.AccountTypeCode);
SubMarket subMarket = gremlinDb.SubMarkets.First(a => a.SubMarketCode == account.SubMarket.SubMarketCode);
account.SubMarket = subMarket;
account.SubMarket = DbHelper.ResolveSubmarket(gremlinDb, account.SubMarket.SubMarketCode);
_ = MetaDataSetter.ForImport(account, "GenericImporter-Method");
//account.DataVersionComment = "Initial import by CSV Importer (Function DbHelper.ImportAccountsFromCSV)";
}
gremlinDb.Accounts.AddRange(accountsReadFromFile);
_ = gremlinDb.SaveChanges();
//Bestätigung senden
Console.WriteLine($"Es wurden {accountsReadFromFile.Count} Accounts erfolgreich der Datenbank hinzugefügt.");
return true;
}
private static bool ImportProductLine(TextFieldParser csvParser, Dictionary<string, int> mappingTable) {
//foreach line in file:
//read seed data, parse/split, save to object with metadata
//add object to list
//add list to context, savechanges
List<ProductLine> productLinesReadFromFile = new(50);
using (csvParser) {
while (!csvParser.EndOfData) {
ProductLine importedProductLine = new();
string[] fields = csvParser.ReadFields()!;
importedProductLine.ProductLineCode = fields[mappingTable["ProductLineCode"]];
importedProductLine.ProductLineDescription = fields[mappingTable["ProductLineDescription"]];
productLinesReadFromFile.Add(importedProductLine);
_ = MetaDataSetter.ForImport(importedProductLine, "GenericImporter-Method");
}
}
gremlinDb.ProductLines.AddRange(productLinesReadFromFile);
_ = gremlinDb.SaveChanges();
return true;
}
private static bool ImportAccountType(TextFieldParser csvParser, Dictionary<string, int> mappingTable) {
//foreach line in file:
//read seed data, parse/split, save to object with metadata
//add object to list
//add list to context, savechanges
List<AccountType> accountTypesReadFromFile = new(20);
using (csvParser) {
while (!csvParser.EndOfData) {
AccountType importedAccountType = new();
string[] fields = csvParser.ReadFields()!;
importedAccountType.AccountTypeCode = fields[mappingTable["AccountTypeCode"]];
importedAccountType.AccountTypeDescription = fields[mappingTable["AccountTypeDescription"]];
accountTypesReadFromFile.Add(importedAccountType);
_ = MetaDataSetter.ForImport(importedAccountType, "GenericImporter-Method");
}
}
gremlinDb.AccountTypes.AddRange(accountTypesReadFromFile);
_ = gremlinDb.SaveChanges();
return true;
}
private static bool ImportSubMarket(TextFieldParser csvParser, Dictionary<string, int> mappingTable) {
//foreach line in file:
//read seed data, parse/split, save to object with metadata
//add object to list
//add list to context, savechanges
List<SubMarket> subMarketsReadFromFile = new(20);
using (csvParser) {
while (!csvParser.EndOfData) {
SubMarket importedSubMarket = new();
string[] fields = csvParser.ReadFields()!;
importedSubMarket.SubMarketCode = fields[mappingTable["SubMarketCode"]];
importedSubMarket.SubMarketDescription = fields[mappingTable["SubMarketDescription"]];
subMarketsReadFromFile.Add(importedSubMarket);
_ = MetaDataSetter.ForImport(importedSubMarket, "GenericImporter-Method");
}
}
gremlinDb.SubMarkets.AddRange(subMarketsReadFromFile);
_ = gremlinDb.SaveChanges();
return true;
}
//private static void SetProperty(object gremlinEntity, PropertyInfo propertyInfo, string value)
//{
// propertyInfo.SetValue(gremlinEntity, value);
//}
//private static Array GetNewGremlinTypeArray(Assembly assembly, string detectedDataType, int length)
//{
// Type GremlinType = GetNewGremlinType(assembly, detectedDataType).GetType();
// //Create an one-dimensional array with n+1 or n+2 elements (n = number of datasets, plus heading and/or emptyline at EOF):
// return Array.CreateInstance(GremlinType, length);
// //Das geht alles nicht:
// //List<GremlinType> test = new();
// //List<typeof(GremlinType)> test = new();
// //List<GremlinType.GetType()> test = new();
// ////Das geht:
// ////Create iList:
// //Type listType = typeof(List<>).MakeGenericType(GremlinType);
// //var list = (IList)Activator.CreateInstance(listType);
// //list.Add(new Account() { AccountName = "Test" });
// //list.Add(GremlinClassObject);
//}
//private static object GetNewGremlinType(Assembly Gremlin, string detectedDataType)
//{
// return Activator.CreateInstance(Gremlin.ToString(), "Gremlin." + detectedDataType).Unwrap();
//}
private static Dictionary<string, string> ReadMappingDictionaryFromFile() {
Dictionary<string, string> result = new();
string fileInput = FileService.ReadResource("MappingDictionary.txt");
string[] lines = fileInput.Split(Environment.NewLine);
foreach (string line in lines) {
string[] fields = line.Split("|");
result.Add(fields[0], fields[1]);
}
return result;
}
private static Dictionary<string, int> MapDataHeading(string[] headings, Dictionary<string, string> columnPropertyMapping) {
Dictionary<string, int> result = new();
for (int i = 0; i < headings.Length; i++) {
string heading = headings[i].ToLower(CultureInfo.CurrentCulture).Trim().Replace(" ", "").Replace("-", "").Replace("_", "").Replace(".", "").Replace(":", "");
if (columnPropertyMapping.TryGetValue(heading, out string? value)) result.Add(value, i);
}
return result;
}
//private static void BasimodosCodeDump()
//{
// //Unfertiger, generischer Code aus der ImportFile() Methode.
// //Aufbewahren für später, wenn Zeit ist, das ordentlich zu machen.
// //while (!csvParser.EndOfData)
// //{
// // //read
// // //new data instance
// // //convert
// // //validate
// // //set metadata: SetMetadataForImport(IMetadata)
// // //add to list
// // fields = csvParser.ReadFields(); // Read current line fields, pointer moves to the next line.
// // if (fields[0] == "") break; // Am Ende hängt eine leere Zeile, die im Parser einen Fehler auslösen würde.
// // //TO DO: vor der foreach "Account" bevorzugt behandeln (Sonderfall bei LSAG: Liste deduplizieren, und als erstes importieren, da sonst keine Contacts importiert werden können).
// // foreach (string detectedDataType in detectedDataTypes)
// // {
// // //"Account" überspringen, da das vor der foreach schon verarbeitet worden ist.
// // if (detectedDataType == "Account") continue;
// // Array GremlinTypeArray = GetNewGremlinTypeArray(Gremlin, detectedDataType, numberOfLines);
// // Type GremlinType = GetNewGremlinType(Gremlin, detectedDataType).GetType();
// // PropertyInfo[] GremlinTypeProperties = GremlinType.GetProperties();
// // foreach (PropertyInfo property in GremlinTypeProperties)
// // {
// // if (mappingTable.TryGetValue(property.Name, out int columnNumber))
// // {
// // //convert:
// // GremlinTypeConverter.Convert(fields[columnNumber]);
// // }
// // }
// // }
// //}
// //using (GremlinContext db = new())
// //{
// // //add to context
// // //savechanges
// //}
//}
//#########################################################################################
//Kann gelöscht werden, sobald die generische Funktion zuverlässig funktioniert:
//#########################################################################################
//private static string RecognizeDataStatic(Dictionary<string, int> mappingDictionary)
//{
// //Logik zur Kategorisierung des Datensatzes:
// //Alle verpflichtenden Angaben zu einer Klasse vorhanen?
// //Alle vom Importer erwarteten Angaben vorhanden?
// //Bei den Enums zusätzlich noch proüfem, dass nur zwei Spalten vorhanden sind, sonst könnten LSAG-Daten falsch identifiziert werden.
// //
// //
// //Products
// if (//required by DB:
// mappingDictionary.ContainsKey("ProductNumber")
// && mappingDictionary.ContainsKey("ListPrice")
// //required by importer:
// && mappingDictionary.ContainsKey("Weight")
// //unique identifier:
// && mappingDictionary.ContainsKey("BreakRangeFrom")
// )
// {
// return "Product";
// }
// //LSAG Contact List Tool List
// if (//required by DB:
// mappingDictionary.ContainsKey("SAPAccountNumber")
// && mappingDictionary.ContainsKey("SAPContactNumber")
// && mappingDictionary.ContainsKey("AccountTypeCode")
// && mappingDictionary.ContainsKey("SubMarketCode")
// && mappingDictionary.ContainsKey("LastName")
// && mappingDictionary.ContainsKey("AccountName")
// //required by importer:
// //unique identifier:
// && mappingDictionary.ContainsKey("MA_ProductInterests")
// )
// {
// return "LSAG Contact List Tool List";
// }
// //Accounts
// if (//required by DB:
// mappingDictionary.ContainsKey("AccountName")
// && mappingDictionary.ContainsKey("Street")
// && mappingDictionary.ContainsKey("ZIP")
// && mappingDictionary.ContainsKey("City")
// && mappingDictionary.ContainsKey("PhoneNumber")
// && mappingDictionary.ContainsKey("SAPAccountNumber")
// && mappingDictionary.ContainsKey("AccountCreatedInSAPOn")
// && mappingDictionary.ContainsKey("AccountTypeCode")
// && mappingDictionary.ContainsKey("SubMarketCode")
// //required by importer:
// )
// {
// return "Account";
// }
// //Contacts
// if (//required by DB:
// mappingDictionary.ContainsKey("LastName")
// && mappingDictionary.ContainsKey("SAPContactNumber")
// //required by importer:
// && mappingDictionary.ContainsKey("FirstName")
// && mappingDictionary.ContainsKey("Gender")
// )
// {
// return "Contact";
// }
// //Custom Description
// if (//required by DB:
// mappingDictionary.ContainsKey("Heading")
// //required by importer:
// && mappingDictionary.ContainsKey("ProductNumber")
// && mappingDictionary.ContainsKey("OptionNumber")
// && mappingDictionary.ContainsKey("DescriptionText")
// //unique identifier:
// && mappingDictionary.ContainsKey("CoverletterText")
// )
// {
// return "CustomDescription";
// }
// //Pseudo-Enums AccountTypes, SubMarkets, ProductLines for DB-initializing
// if (mappingDictionary.Count == 2)
// {
// if (mappingDictionary.ContainsKey("AccountTypeCode")
// && mappingDictionary.ContainsKey("AccountTypeDescription")
// && mappingDictionary.Count == 2)
// {
// return "AccountType";
// }
// if (mappingDictionary.ContainsKey("SubMarketCode")
// && mappingDictionary.ContainsKey("SubMarketDescription")
// && mappingDictionary.Count == 2)
// {
// return "SubMarket";
// }
// if (mappingDictionary.ContainsKey("ProductLineCode")
// && mappingDictionary.ContainsKey("ProductLineDescription")
// && mappingDictionary.Count == 2)
// {
// return "ProductLine";
// }
// }
// return "No entity type unambigiously identified!";
//}
}