Gremlin/Gremlin_BlazorServer/Data/DBClasses/GenericImporter.cs

1052 lines
38 KiB
C#

using Gremlin_BlazorServer.Data.EntityClasses;
using Gremlin_BlazorServer.Services;
using Microsoft.VisualBasic.FileIO;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using Gremlin_BlazorServer.Utilities.GUClasses;
using static Gremlin_BlazorServer.Data.EntityClasses.Enums;
namespace Gremlin_BlazorServer.Data.DBClasses
{
public static 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; } = string.Empty;
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 static 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 = new[] { "|", ",", ";" };
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)
{
Debug.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",
Products = new List<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.Intersect(cD.Products).Any()) { 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.Products = productsInDb.Where(product => product.ProductNumber == cD.ProductNumber && product.OptionNumber == cD.OptionNumber).ToList();
//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
Debug.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
Debug.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.ToInt32(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
Debug.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().ToString() + 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
Debug.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!";
//}
}
}