48 Commits

Author SHA1 Message Date
fierke 75b6b1dc4d [chore:] various tiny improvements 2026-05-20 08:00:20 +02:00
fierke 1161a437c4 [fix:] wiki preview reloading after saving changes 2026-05-19 09:44:14 +02:00
fierke 605ba95848 [fix:] message box center screen startup 2026-05-19 09:32:06 +02:00
fierke eb38bafd23 [feat:] delete address sets (FINALLY, this was so hard to implement, it took almost 2 minutes) 2026-05-19 09:31:35 +02:00
fierke e2d5ae2a70 Merge pull request '[fix?:] windows paths (ai-based since windows is boring)' (#57) from windows-paths into main
Reviewed-on: #57

since this still works on any linux machines and theoretical on windows machines, we're merging it to prevent wrong commits cause i configured something wrong
2026-05-19 07:24:01 +00:00
fierke b3ab21ae38 [fix?:] windows paths (ai-based since windows is boring) 2026-05-18 11:42:32 +02:00
fierke 67d007bb31 [fix:] inconsistency with config.json 2026-05-18 11:37:10 +02:00
fierke bf28ba1914 [chore:] logging for StableFontResolver.cs 2026-05-16 18:19:50 +02:00
fierke 40630dee35 [chore:] logging for PdfBuilder.cs 2026-05-16 18:19:36 +02:00
fierke c6baa6a187 [chore:] logging for MarkdownRenderer.cs 2026-05-16 18:19:27 +02:00
fierke 557052bbc7 [chore:] logging for EditorWindow.axaml.cs 2026-05-16 18:19:19 +02:00
fierke 696d0f8fcb [chore:] logging for DataImport.cs 2026-05-16 18:19:09 +02:00
fierke 201b19cbb5 [chore:] logging for CsvBuilder.cs 2026-05-16 18:18:58 +02:00
fierke d7d4b3b31b [file:] removing docs.tex since we're using a markdown wiki 2026-05-16 16:14:31 +00:00
fierke fa10b5c7d0 [chore:] logging for ResultWindow.axaml.cs 2026-05-16 15:10:55 +02:00
fierke 85685d95bb [chore:] logging for NamingWindow.axaml.cs 2026-05-16 15:10:49 +02:00
fierke 8a42f8dc7d [chore:] logging for CombineAddresses.cs 2026-05-16 15:09:55 +02:00
fierke cdc4fb70cd [chore:] logging for AddressShortener.cs 2026-05-16 15:09:50 +02:00
fierke 10dfd1e080 [chore:] logging for AddressRepair.cs 2026-05-16 15:09:45 +02:00
fierke 4b98f53881 [chore:] logging for AddressPatch.cs 2026-05-16 15:09:38 +02:00
fierke 5f79df55d2 [chore:] logging for AddressCreation.cs 2026-05-16 15:09:32 +02:00
fierke 2d33326bab [chore:] logging for AddressCheck.cs 2026-05-16 15:09:23 +02:00
fierke b48652910e [fix:] format errors 2026-05-16 14:44:38 +02:00
fierke a74a97559b [fix:] typo 2026-05-16 14:42:28 +02:00
fierke b5b6dc8de7 [chore:] basic saving logging 2026-05-16 14:41:28 +02:00
fierke 6dfab5e73a [chore:] basic logging 2026-05-16 14:41:16 +02:00
fierke 6f03a26c29 [fix:] console logging printed an array 👀 2026-05-16 14:41:06 +02:00
fierke 7c81920e84 [init:] logging system 2026-05-16 14:12:16 +02:00
fierke d337f94851 [fix?:] trying 2026-05-15 18:56:55 +02:00
fierke 1922b30ada [fix?:] will this work? 2026-05-15 18:41:45 +02:00
fierke 68541621d9 [fix?:] windows file path for label export? 2026-05-15 18:34:25 +02:00
fierke 474b628f0b [chore:] renaming address sets now gives the current name as a base 2026-05-15 17:51:35 +02:00
fierke 7fe45ce4e3 [feat:] csv-divider-buttons 2026-05-06 07:07:41 +02:00
fierke a46539c3cc [fix:] wrong sorting of imported address sets 2026-05-06 06:35:23 +02:00
fierke 72a5630db1 [chore:] enable shortener usage 2026-05-06 06:34:48 +02:00
fierke 7697edac5e [fix:] shorten-button was enabled too early 2026-05-06 06:33:55 +02:00
fierke 028b9793db [chore:] basic address shortener 2026-05-06 06:33:37 +02:00
fierke 2c5f2ed48b [feat:] (opt) delete original sets after merging 2026-05-03 09:21:57 +02:00
fierke f0598a39ec [chore:] listbox item type is no string anymore (hopefully I tested everything?) 2026-05-02 16:27:43 +02:00
fierke f9e419d573 [chore:] cleanup 2026-05-02 16:20:18 +02:00
fierke 56233e6f5c [chore:] tiny improvements for address check 2026-05-02 15:39:42 +02:00
fierke 488830cdad [fix:] multiple little fixed for the combining methods 2026-05-02 12:56:27 +02:00
fierke 52fbefb803 [chore:] added missing help- and about-links 2026-04-29 14:58:53 +02:00
fierke c5aabc2a02 [fix:] window focus 2026-04-28 11:00:51 +02:00
fierke 4a9f9a1ff0 [fix:] startup location 2026-04-28 11:00:40 +02:00
fierke fab14eb107 [fix:] naming window for rest-set not visible if not necessary 2026-04-28 11:00:26 +02:00
fierke 2dcc1bd657 Merge remote-tracking branch 'origin/main' 2026-04-28 10:54:09 +02:00
fierke 3767fece48 [fix:] make customer delete button working 2026-04-28 10:53:53 +02:00
26 changed files with 2282 additions and 1798 deletions
+22
View File
@@ -60,6 +60,11 @@ public class KasAddressList //Address-Set
var id = listItemName.Split(" - ")[0]; var id = listItemName.Split(" - ")[0];
return int.Parse(id); return int.Parse(id);
} }
public override string ToString()
{
return Name + " (" + KasPersons.Count + " Einträge)";
}
} }
public class KasPerson public class KasPerson
@@ -188,6 +193,18 @@ public class KasPerson
return highest + base_id + 1; return highest + base_id + 1;
} }
public override string ToString()
{
if (refsid != null && refsid != 0)
{
return refsid + " - " + name;
}
else
{
return id + " - " + name;
}
}
} }
public class KasPersonError public class KasPersonError
@@ -212,4 +229,9 @@ public class KasPersonError
return output; return output;
} }
public string ToString(KasPerson person)
{
return "ID:"+person.id + "; Name: " +person.name +"; Errors: " + GetString();
}
} }
+37 -25
View File
@@ -12,8 +12,8 @@ public class Settings
public Customers customers = new(); public Customers customers = new();
public PdfExportSettings pdfExport { get; set; } = new(); public PdfExportSettings pdfExport { get; set; } = new();
public string settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), // public string settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"logofclient", "config.json"); // "logofclient", "config.json");
public Settings() public Settings()
{ {
@@ -23,31 +23,28 @@ public class Settings
public static void Save() public static void Save()
{ {
if (!Directory.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), if (!Directory.Exists(Global._instance.config_path))
"logofclient"))) Directory.CreateDirectory(Global._instance.config_path);
Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), // if (!string.IsNullOrEmpty(Global._instance.config_path)) _instance.settingsPath = Global._instance.config_path;
"logofclient"));
if (!string.IsNullOrEmpty(Global._instance.config_path)) _instance.settingsPath = Global._instance.config_path;
var json = JsonConvert.SerializeObject(_instance); var json = JsonConvert.SerializeObject(_instance);
File.WriteAllText(_instance.settingsPath, json); File.WriteAllText(Path.Combine(Global._instance.config_path,"config.json"), json);
} }
public static void Load() public static void Load()
{ {
if (!string.IsNullOrEmpty(Global._instance.config_path)) _instance.settingsPath = Global._instance.config_path; //if (!string.IsNullOrEmpty(Global._instance.config_path)) _instance.settingsPath = Global._instance.config_path;
try try
{ {
var contents = File.ReadAllText(_instance.settingsPath); var contents = File.ReadAllText(Path.Combine(Global._instance.config_path, "config.json"));
_instance = JsonConvert.DeserializeObject<Settings>(contents); _instance = JsonConvert.DeserializeObject<Settings>(contents);
MainWindow._instance.RefreshCustomerItems(); MainWindow._instance.RefreshCustomerItems();
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex.Message); Logger.Log($"Error while reading settings. Generating new... {ex.Message}", Logger.LogType.Warning);
Console.WriteLine(ex.StackTrace);
Console.WriteLine("Error while reading settings. Generating new...");
_instance = new Settings(); _instance = new Settings();
} }
} }
@@ -83,9 +80,13 @@ public class Global
public string config_path { get; set; } = Path.Combine( public string config_path { get; set; } = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"logofclient", "logofclient");
"config.json");
public void SetConfigPath(string path)
{
if (!string.IsNullOrWhiteSpace(path))
config_path = PathUtilities.NormalizeFileSystemPath(path);
}
public string wiki_storage_path { get; set; } = Path.Combine( public string wiki_storage_path { get; set; } = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"logofclient", "logofclient",
@@ -119,23 +120,29 @@ public class Global
var contents = File.ReadAllText(Path.Combine( var contents = File.ReadAllText(Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "logofclient", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "logofclient",
"global.config")); "global.config"));
_instance = JsonConvert.DeserializeObject<Global>(contents); _instance = JsonConvert.DeserializeObject<Global>(contents) ?? new Global();
_instance.NormalizePaths();
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex.Message); Logger.Log($"Error while reading global settings. Generating new... {ex.Message}", Logger.LogType.Warning);
Console.WriteLine(ex.StackTrace);
Console.WriteLine("Error while reading global settings. Generating new...");
_instance = new Global(); _instance = new Global();
Save(); Save();
} }
} }
private void NormalizePaths()
{
config_path = PathUtilities.NormalizeFileSystemPath(config_path);
wiki_storage_path = PathUtilities.NormalizeFileSystemPath(wiki_storage_path);
font_path = PathUtilities.NormalizeFileSystemPath(font_path);
}
} }
public class Customers public class Customers
{ {
public List<Customer> customers = new(); public List<Customer> customers = new();
public int current { get; set; } = 0; public Customer current { get; set; } = null;
} }
public class Customer public class Customer
@@ -150,6 +157,11 @@ public class Customer
ID = highestID + 1; ID = highestID + 1;
} }
public override string ToString()
{
return name;
}
public string name { get; set; } = ""; public string name { get; set; } = "";
public string description { get; set; } = ""; public string description { get; set; } = "";
public string sender_address { get; set; } = ""; public string sender_address { get; set; } = "";
@@ -158,11 +170,11 @@ public class Customer
public int ID { get; } public int ID { get; }
public static int GetIDByCustomerListItem(string item_content) // public static int GetIDByCustomerListItem(string item_content)
{ // {
var id = item_content.Split(" - ")[0]; // var id = item_content.Split(" - ")[0];
return int.Parse(id); // return int.Parse(id);
} // }
} }
public class AddressSets public class AddressSets
+40
View File
@@ -0,0 +1,40 @@
using System;
using System.IO;
using Newtonsoft.Json;
namespace Logof_Client;
public static class Logger
{
public static void Log(string text, LogType logType = LogType.Info)
{
try
{
string config_path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"logofclient");
if (!Directory.Exists(config_path))
Directory.CreateDirectory(config_path);
string log_path = Path.Combine(config_path, $"log-{DateTime.Now:dd-MM-yy}.log");
if(!File.Exists(log_path))
File.Create(log_path).Close();
string line = $"[{DateTime.Now:dd.MM.yyyy - hh:mm:ss}]: ({logType.ToString()}) {text}";
Console.WriteLine(line);
File.AppendAllLines(log_path, [line]);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public enum LogType
{
Error,
Warning,
Info
}
}
+24 -8
View File
@@ -78,7 +78,7 @@
</StackPanel> </StackPanel>
</MenuItem.Header> </MenuItem.Header>
</MenuItem> </MenuItem>
<MenuItem x:Name="MnIAdSetDelete" Click="MnIAdSetRename_OnClick" IsEnabled="False"> <MenuItem x:Name="MnIAdSetDelete" Click="MnIAdSetDelete_OnClick" IsEnabled="True">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<LucideIcon Kind="Trash" Width="12" Height="12" Size="12" /> <LucideIcon Kind="Trash" Width="12" Height="12" Size="12" />
@@ -121,7 +121,7 @@
FontWeight="Bold" /> FontWeight="Bold" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button Width="250" IsEnabled="False" <Button Width="250" IsEnabled="False" Click="BtnShorten_OnClick"
HorizontalContentAlignment="Center" x:Name="BtnShorten" HorizontalContentAlignment="Center" x:Name="BtnShorten"
Margin="0,0,0,10"> Margin="0,0,0,10">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@@ -220,10 +220,18 @@
</StackPanel> </StackPanel>
<Label FontSize="9" Content="Nur Elemente, die NICHT doppelt sind" /> <Label FontSize="9" Content="Nur Elemente, die NICHT doppelt sind" />
</StackPanel> </StackPanel>
</Button> </Button>
</StackPanel> </StackPanel>
<CheckBox HorizontalAlignment="Center" x:Name="CbMergeExportUnmerged" IsChecked="False">Speichere Unverarbeitete in neuem Verteiler</CheckBox> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="50">
<StackPanel Orientation="Vertical" >
<RadioButton Content="Vergleiche nach refsid" IsChecked="True" x:Name="RbComprefsid"></RadioButton>
<RadioButton Content="Vergleiche nach finaler Adresse" IsChecked="False" x:Name="RbCompfinAd"></RadioButton>
</StackPanel>
<StackPanel Orientation="Vertical">
<CheckBox HorizontalAlignment="Left" x:Name="CbMergeExportUnmerged" IsChecked="False">Speichere Unverarbeitete in neuem Verteiler</CheckBox>
<CheckBox HorizontalAlignment="Left" x:Name="CbMergeDeleteOld" IsChecked="False">Lösche ursprüngliche Sets</CheckBox>
</StackPanel>
</StackPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
<Grid Grid.Row="2" Margin="20" ColumnDefinitions="*,5*,*" IsVisible="False" x:Name="GrdExportMarginOptions"> <Grid Grid.Row="2" Margin="20" ColumnDefinitions="*,5*,*" IsVisible="False" x:Name="GrdExportMarginOptions">
@@ -381,7 +389,7 @@
<StackPanel Grid.Column="1" Orientation="Vertical" Spacing="5"> <StackPanel Grid.Column="1" Orientation="Vertical" Spacing="5">
<StackPanel Orientation="Horizontal" Spacing="5"> <StackPanel Orientation="Horizontal" Spacing="5">
<TextBox x:Name="TbConfigPath" HorizontalAlignment="Stretch" <TextBox x:Name="TbConfigPath" HorizontalAlignment="Stretch"
Watermark="/home/username/.config/logofclient/config.json" /> Watermark="/home/username/.config/logofclient/" />
<Button x:Name="BtnConfigPath" HorizontalAlignment="Right"> <Button x:Name="BtnConfigPath" HorizontalAlignment="Right">
<Button.Content> <Button.Content>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@@ -477,12 +485,20 @@
TextChanged="TbSettingsCustomerSenderAddress_OnTextChanged" TextChanged="TbSettingsCustomerSenderAddress_OnTextChanged"
x:Name="TbSettingsCustomerSenderAddress" /> x:Name="TbSettingsCustomerSenderAddress" />
</Grid> </Grid>
<Grid ColumnDefinitions="150,*"> <Grid ColumnDefinitions="150,*,Auto">
<Label Content="CSV-Trennzeichen" /> <Label Content="CSV-Trennzeichen" />
<TextBox Grid.Column="1" Watermark="," <TextBox Grid.Column="1" Watermark=","
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0,0,5,0"
TextChanged="TbSettingsCustomerCsvSeparator_OnTextChanged" TextChanged="TbSettingsCustomerCsvSeparator_OnTextChanged"
x:Name="TbSettingsCustomerCsvSeparator" /> x:Name="TbSettingsCustomerCsvSeparator" />
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="5">
<Button Click="ChangeCSVDivider" Content=","></Button>
<Button Click="ChangeCSVDivider" Content=";"></Button>
<Button Click="ChangeCSVDivider" Content="TAB"></Button>
<Button Click="ChangeCSVDivider" Content="|"></Button>
<Button Click="ChangeCSVDivider" Content="SPACE"></Button>
<Button Click="ChangeCSVDivider" Content=":"></Button>
</StackPanel>
</Grid> </Grid>
<Grid ColumnDefinitions="150,*"> <Grid ColumnDefinitions="150,*">
<Label Content="Address-Patch-Info" /> <Label Content="Address-Patch-Info" />
@@ -509,7 +525,7 @@
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button Background="#99963434" HorizontalAlignment="Stretch" <Button Background="#99963434" HorizontalAlignment="Stretch" x:Name="BtnDeleteCustomer" Click="BtnDeleteCustomer_OnClick"
HorizontalContentAlignment="Center"> HorizontalContentAlignment="Center">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<LucideIcon Kind="Trash" Width="16" Height="16" Size="16" /> <LucideIcon Kind="Trash" Width="16" Height="16" Size="16" />
+350 -195
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" SizeToContent="WidthAndHeight" mc:Ignorable="d" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen"
x:Class="Logof_Client.MessageBox" Icon="assets/icon.ico" x:Class="Logof_Client.MessageBox" Icon="assets/icon.ico"
Title="MessageBox"> Title="MessageBox">
<StackPanel> <StackPanel>
+1 -1
View File
@@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" SizeToContent="WidthAndHeight" mc:Ignorable="d" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" Topmost="True"
x:Class="Logof_Client.NamingWindow" x:Class="Logof_Client.NamingWindow"
Title="NamingWindow"> Title="NamingWindow">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
+3 -1
View File
@@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
@@ -45,12 +46,13 @@ public partial class NamingWindow : Window
if (parent != null) if (parent != null)
wind.ShowDialog(parent); wind.ShowDialog(parent);
else wind.Show(); else wind.Show();
wind.Focus();
return tcs.Task; return tcs.Task;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("Error while showing naming window: " + ex.Message); Logger.Log("Error while showing naming window: " + ex.Message, Logger.LogType.Warning);
return Task.FromResult<string>(null!); return Task.FromResult<string>(null!);
} }
} }
+1 -1
View File
@@ -3,7 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" Width="800" MinWidth="800" MaxWidth="800" d:DesignHeight="150" mc:Ignorable="d" d:DesignWidth="800" Width="800" MinWidth="800" MaxWidth="800" d:DesignHeight="150"
Height="150" MinHeight="150" MaxHeight="150" Icon="assets/icon.ico" Height="150" MinHeight="150" MaxHeight="150" Icon="assets/icon.ico" WindowStartupLocation="CenterScreen" Topmost="True"
x:Class="Logof_Client.ProgressWindow" Title="Verarbeitung läuft..."> x:Class="Logof_Client.ProgressWindow" Title="Verarbeitung läuft...">
<Grid> <Grid>
<!-- <ScrollViewer x:Name="ScvLog"> --> <!-- <ScrollViewer x:Name="ScvLog"> -->
+2 -2
View File
@@ -16,9 +16,9 @@ public partial class ProgressWindow : Window
PbProgress.Value = percentage; PbProgress.Value = percentage;
} }
public void AddToLog(string message) public void AddToLog(string message, string percent)
{ {
TbLog.Text = message; TbLog.Text = message + $"\n{percent}%";
//ScvLog.ScrollToEnd(); //ScvLog.ScrollToEnd();
} }
} }
+3 -1
View File
@@ -20,7 +20,9 @@
Margin="10,10,10,10" /> Margin="10,10,10,10" />
</Grid> </Grid>
<ScrollViewer Grid.Column="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <ScrollViewer Grid.Column="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="StkResults" Orientation="Vertical" Margin="10" /> <!-- <TextBlock x:Name="TbResults"></TextBlock> -->
<ListBox x:Name="LbResults"></ListBox>
<!-- <StackPanel x:Name="StkResults" Orientation="Vertical" Margin="10" /> -->
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>
+36 -4
View File
@@ -28,12 +28,25 @@ public partial class ResultWindow : Window
private void GenerateView(List<KasPerson> result) private void GenerateView(List<KasPerson> result)
{ {
try
{
// Filter to only show persons with errors // Filter to only show persons with errors
var result_with_errors = result.Where(p => p.PersonError != null).ToList(); var result_with_errors = result.Where(p => p.PersonError != null).ToList();
LblResultCount.Content = $"{result_with_errors.Count}/{ur_result.Count} Ergebnisse"; LblResultCount.Content = $"{result_with_errors.Count}/{ur_result.Count} Ergebnisse";
StkResults.Children.Clear(); // TbResults.Text = "";
foreach (var person in result_with_errors) StkResults.Children.Add(CreatePersonGrid(person)); // foreach (var person in result_with_errors) TbResults.Text += person.PersonError.GetString()+"\n";
LbResults.Items.Clear();
foreach (var person in result_with_errors) LbResults.Items.Add(person.PersonError.ToString(person));
// StkResults.Children.Clear();
// foreach (var person in result_with_errors) StkResults.Children.Add(CreatePersonGrid(person));
}
catch (Exception ex)
{
Logger.Log("Error while generating result view: " + ex.Message, Logger.LogType.Warning);
}
} }
private Grid CreatePersonGrid(KasPerson person) private Grid CreatePersonGrid(KasPerson person)
@@ -101,6 +114,8 @@ public partial class ResultWindow : Window
} }
private void Load(List<KasPerson> result) private void Load(List<KasPerson> result)
{
try
{ {
var knownErrors = new List<AddressCheck.ErrorTypes>(); var knownErrors = new List<AddressCheck.ErrorTypes>();
var knownWarnings = new List<AddressCheck.WarningTypes>(); var knownWarnings = new List<AddressCheck.WarningTypes>();
@@ -141,12 +156,20 @@ public partial class ResultWindow : Window
GenerateView(result); GenerateView(result);
} }
catch (Exception ex)
{
Logger.Log("Error while showing naming window: " + ex.Message, Logger.LogType.Warning);
}
}
private void BtnUpdateFilter_OnClick(object? sender, RoutedEventArgs e) private void BtnUpdateFilter_OnClick(object? sender, RoutedEventArgs e)
{ {
} }
private void UpdateFilter() private void UpdateFilter()
{
try
{ {
var temp_result = new List<KasPerson>(); var temp_result = new List<KasPerson>();
@@ -201,8 +224,17 @@ public partial class ResultWindow : Window
LblResultCount.Content = $"{temp_result.Count}/{ur_result.Count} Ergebnisse"; LblResultCount.Content = $"{temp_result.Count}/{ur_result.Count} Ergebnisse";
StkResults.Children.Clear(); LbResults.Items.Clear();
foreach (var person in temp_result) StkResults.Children.Add(CreatePersonGrid(person)); foreach (var person in temp_result) LbResults.Items.Add(person.PersonError.ToString(person));
// TbResults.Text = "";
// foreach (var person in temp_result) TbResults.Text += person.PersonError.GetString() +"\n";
// StkResults.Children.Clear();
// foreach (var person in temp_result) StkResults.Children.Add(CreatePersonGrid(person));
} catch (Exception ex)
{
Logger.Log("Error while updating filter: " + ex.Message, Logger.LogType.Warning);
}
} }
+16 -2
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@@ -37,6 +38,8 @@ public class AddressCheck
} }
public async Task<List<KasPerson>> Perform(int id) public async Task<List<KasPerson>> Perform(int id)
{
try
{ {
// Find the index of the address set with the given id // Find the index of the address set with the given id
var adset_index = -1; var adset_index = -1;
@@ -237,7 +240,8 @@ public class AddressCheck
// Directly set PersonError in the address set // Directly set PersonError in the address set
var person_index = adset.KasPersons.IndexOf(person); var person_index = adset.KasPersons.IndexOf(person);
if (person_index >= 0) if (person_index >= 0)
Settings._instance.addressSets.addresses[adset_index].KasPersons[person_index].PersonError = Settings._instance.addressSets.addresses[adset_index].KasPersons[person_index]
.PersonError =
new KasPersonError((errors, warnings)); new KasPersonError((errors, warnings));
} }
@@ -247,7 +251,8 @@ public class AddressCheck
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
{ {
if (hasFaults) if (hasFaults)
_progress.AddToLog($"Person mit id {person.id} ist fehlerhaft"); _progress.AddToLog($"Person mit id {person.id} ist fehlerhaft",
Convert.ToInt32(percent).ToString());
_progress.ChangePercentage(percent); _progress.ChangePercentage(percent);
}); });
@@ -260,5 +265,14 @@ public class AddressCheck
return Settings._instance.addressSets.addresses[adset_index].KasPersons return Settings._instance.addressSets.addresses[adset_index].KasPersons
.Where(p => p.PersonError != null) .Where(p => p.PersonError != null)
.ToList(); .ToList();
} }
catch (Exception ex)
{
Logger.Log($"Error while performing address check: {ex.Message}", Logger.LogType.Error);
}
return null;
}
} }
+29 -1
View File
@@ -43,7 +43,8 @@ public static class AddressCreator
public static string? CreateFinalMarkdownString(int id) public static string? CreateFinalMarkdownString(int id)
{ {
// Maximum seven lines of information // Maximum seven lines of information
try
{
// find the address // find the address
KasPerson? address = null; KasPerson? address = null;
var string_address = ""; var string_address = "";
@@ -197,9 +198,18 @@ public static class AddressCreator
if (address_line_count > 1) return string_address; if (address_line_count > 1) return string_address;
return null; return null;
} }
catch (Exception ex)
{
Logger.Log($"Error while creating markdown string: {ex.Message}", Logger.LogType.Error);
}
return null;
}
public static string CreateNameLine(string anredezus, string anrede, string titel, string vorname, string adel, public static string CreateNameLine(string anredezus, string anrede, string titel, string vorname, string adel,
string name, string namezus) string name, string namezus)
{
try
{ {
if (!string.IsNullOrWhiteSpace(anredezus)) if (!string.IsNullOrWhiteSpace(anredezus))
return string.Join(" ", return string.Join(" ",
@@ -215,6 +225,14 @@ public static class AddressCreator
) )
+ (string.IsNullOrWhiteSpace(namezus) ? "" : $" ({namezus.Trim()})"); + (string.IsNullOrWhiteSpace(namezus) ? "" : $" ({namezus.Trim()})");
} }
catch (Exception ex)
{
Logger.Log($"Error while performing address name line creation: {ex.Message}", Logger.LogType.Error);
}
return null;
}
/// <summary> /// <summary>
/// Returns true if a plz (or pplz) is valid /// Returns true if a plz (or pplz) is valid
@@ -223,6 +241,8 @@ public static class AddressCreator
/// <param name="land">country, to check the plz</param> /// <param name="land">country, to check the plz</param>
/// <returns>true or faslse, depending on result :+1:</returns> /// <returns>true or faslse, depending on result :+1:</returns>
public static bool CheckPLZ(string plz, string land) public static bool CheckPLZ(string plz, string land)
{
try
{ {
if (string.IsNullOrWhiteSpace(plz)) return false; if (string.IsNullOrWhiteSpace(plz)) return false;
@@ -249,4 +269,12 @@ public static class AddressCreator
// For non-German countries, accept any non-empty postal code // For non-German countries, accept any non-empty postal code
return true; return true;
} }
catch (Exception ex)
{
Logger.Log($"Error while performing address plz check: {ex.Message}", Logger.LogType.Error);
}
return false;
}
} }
+22
View File
@@ -3,6 +3,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using Logof_Client;
public class AddressPatch public class AddressPatch
{ {
@@ -57,6 +58,8 @@ public class AddressPatch
public bool has_funktionad { get; set; } public bool has_funktionad { get; set; }
public static AddressPatch Import(Uri filename) public static AddressPatch Import(Uri filename)
{
try
{ {
var patch = new AddressPatch(); var patch = new AddressPatch();
@@ -92,9 +95,20 @@ public class AddressPatch
return patch; return patch;
} }
catch (Exception ex)
{
Logger.Log($"Error while importing address patch: {ex.Message}", Logger.LogType.Error);
}
return null;
}
public override string ToString() public override string ToString()
{
try
{ {
var properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); var properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
var stringProps = properties.Where(p => p.PropertyType == typeof(string) && p.Name.EndsWith("_is")); var stringProps = properties.Where(p => p.PropertyType == typeof(string) && p.Name.EndsWith("_is"));
@@ -115,4 +129,12 @@ public class AddressPatch
return lines.ToString().TrimEnd(); return lines.ToString().TrimEnd();
} }
catch (Exception ex)
{
Logger.Log($"Error while parsing: {ex.Message}", Logger.LogType.Error);
}
return "Error while parsing";
}
} }
+11 -9
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Logof_Client; namespace Logof_Client;
@@ -9,15 +10,16 @@ public class AddressRepair(ProgressWindow progressWindow)
public KasAddressList Perform(KasAddressList all_addresses, public KasAddressList Perform(KasAddressList all_addresses,
List<(int, List<AddressCheck.ErrorTypes>)> failed_addresses) List<(int, List<AddressCheck.ErrorTypes>)> failed_addresses)
{ {
// foreach (var k in all_addresses.KasPersons) try
// foreach (var p in failed_addresses) {
// {
// if (k.refsid != p.Item1) continue; }
// catch (Exception ex)
// if (p.Item1.Contains(AddressCheck.WarningTypes.DoubledRefsid)) {
// { Logger.Log($"Error while performing address repair: {ex.Message}", Logger.LogType.Error);
// } }
// }
return null;
return null; return null;
} }
+84
View File
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
namespace Logof_Client;
public class AddressShortener(ProgressWindow progressWindow)
{
private readonly ProgressWindow _progress = progressWindow;
public async Task Perform(KasAddressList list)
{
try
{
List<int> doubled_ids = new List<int>();
for (int i = 0; i < list.KasPersons.Count; i++)
{
var address = list.KasPersons[i];
for (int j = 0; j < list.KasPersons.Count; j++)
{
if (i == j) continue;
var sec_address = list.KasPersons[j];
if (address.refsid == sec_address.refsid && !doubled_ids.Contains(address.refsid) && address.refsid != 0)
{
doubled_ids.Add(address.refsid);
}
}
}
// delete doubled addresses by refsid
foreach (int id in doubled_ids)
{
// does this remove both of the doubled addresses?
list.KasPersons.Remove(list.KasPersons.FirstOrDefault(x => x.refsid == id));
}
List<int> toRemove = new List<int>();
foreach (var address in list.KasPersons)
{
try
{
if (address.PersonError.errors.Contains(AddressCheck.ErrorTypes.NoPLZorPPLZ))
{
toRemove.Add(address.id);
}
else if (address.PersonError.errors.Contains(AddressCheck.ErrorTypes.PlzNotUsable) &&
address.PersonError.errors.Contains(AddressCheck.ErrorTypes.PPlzNotUsable))
{
toRemove.Add(address.id);
} else if (address.PersonError.errors.Contains(AddressCheck.ErrorTypes.PlzNotUsable) &&
address.PersonError.warnings.Contains(AddressCheck.WarningTypes.NoPPLZ))
{
toRemove.Add(address.id);
}
else if (address.PersonError.errors.Contains(AddressCheck.ErrorTypes.PPlzNotUsable) &&
address.PersonError.warnings.Contains(AddressCheck.WarningTypes.NoPLZ))
{
toRemove.Add(address.id);
}
}
catch
{
Console.WriteLine("PersonError not accessible: " + address.id);
}
}
// delete doubled addresses by refsid
foreach (int id in toRemove)
{
// does this remove both of the doubled addresses?
list.KasPersons.Remove(list.KasPersons.Find(x => x.id == id));
}
}
catch (Exception ex)
{
Logger.Log($"Error while performing address shortener: {ex.Message}", Logger.LogType.Error);
}
}
}
+110 -78
View File
@@ -16,15 +16,58 @@ public class CombineAddresses
_progress = progressWindow; _progress = progressWindow;
} }
public async Task<(KasAddressList, KasAddressList)> Perform(List<KasAddressList> address_lists, string type, public enum CombineType
bool? exportUnused)
{ {
if (type == "difference") return await Difference(address_lists, exportUnused); refsid,
if (type == "union") return await Union(address_lists, exportUnused); final_adress,
if (type == "intersection") return await Intersection(address_lists, exportUnused); none
if (type == "symdiff") return await SymmetricDifference(address_lists, exportUnused); }
public async Task<(KasAddressList, KasAddressList)> Perform(List<KasAddressList> address_lists, string type, CombineType comb_type,
bool? exportUnused, bool? deleteOld)
{
try
{
var result = await Execute(address_lists,type,comb_type,exportUnused);
if (deleteOld == true)
{
foreach (var list in address_lists)
{
Settings._instance.addressSets.addresses.Remove(list);
}
}
return result;
}
catch (Exception ex)
{
Logger.Log($"Error while performing address combining: {ex.Message}", Logger.LogType.Error);
}
return (null,null); return (null,null);
}
private async Task<(KasAddressList, KasAddressList)> Execute(List<KasAddressList> address_lists, string type, CombineType comb_type,
bool? exportUnused)
{
try
{
if (type == "difference") return await Difference(address_lists, comb_type, exportUnused);
if (type == "union") return await Union(address_lists, comb_type, exportUnused);
if (type == "intersection") return await Intersection(address_lists, comb_type, exportUnused);
if (type == "symdiff") return await SymmetricDifference(address_lists, comb_type, exportUnused);
return (null, null);
}
catch (Exception ex)
{
Logger.Log($"Error while executing address combining: {ex.Message}", Logger.LogType.Error);
}
return (null,null);
} }
@@ -35,11 +78,13 @@ public class CombineAddresses
/// <param name="second">Second address to compare</param> /// <param name="second">Second address to compare</param>
/// <param name="only_refsid">If true, only a refsid-check will be done</param> /// <param name="only_refsid">If true, only a refsid-check will be done</param>
/// <returns></returns> /// <returns></returns>
public bool CompareAddresses(KasPerson first, KasPerson second, bool only_refsid = false) public bool CompareAddresses(KasPerson first, KasPerson second, CombineType comb_type)
{ {
// A refsid of 0 means "missing", so it must not collapse unrelated entries. // A refsid of 0 means "missing", so it must not collapse unrelated entries.
if(comb_type == CombineType.refsid)
if (first.refsid != 0 && second.refsid != 0 && first.refsid == second.refsid) return true; if (first.refsid != 0 && second.refsid != 0 && first.refsid == second.refsid) return true;
if (!only_refsid) if (comb_type == CombineType.final_adress)
if (first.name == second.name && if (first.name == second.name &&
first.anrede == second.anrede && first.anrede == second.anrede &&
first.anredzus == second.anredzus && first.anredzus == second.anredzus &&
@@ -68,9 +113,11 @@ public class CombineAddresses
return false; return false;
} }
public async Task<(KasAddressList, KasAddressList)> Difference(List<KasAddressList> address_lists, public async Task<(KasAddressList, KasAddressList)> Difference(List<KasAddressList> address_lists, CombineType comb_type,
bool? return_unused, bool? return_unused,
Progress? progress = null) Progress? progress = null)
{
try
{ {
if (address_lists == null || address_lists.Count == 0) if (address_lists == null || address_lists.Count == 0)
return (new KasAddressList(await KasAddressList.GenerateName("difference")), null); return (new KasAddressList(await KasAddressList.GenerateName("difference")), null);
@@ -86,11 +133,13 @@ public class CombineAddresses
for (var i = 1; i < address_lists.Count; i++) for (var i = 1; i < address_lists.Count; i++)
restUnion.AddRange(address_lists[i].KasPersons); restUnion.AddRange(address_lists[i].KasPersons);
var result = new KasAddressList(await KasAddressList.GenerateName("difference")); var result = new KasAddressList(await KasAddressList.GenerateName("difference"));
var second_result = new KasAddressList(await KasAddressList.GenerateName("difference_rest", false)); var second_result = new KasAddressList("none");
if(return_unused == true)
second_result = new KasAddressList(await KasAddressList.GenerateName("difference_rest", false));
foreach (var person in address_lists[0].KasPersons) foreach (var person in address_lists[0].KasPersons)
{ {
var isDouble = restUnion.Any(p => CompareAddresses(person, p)); var isDouble = restUnion.Any(p => CompareAddresses(person, p, comb_type));
if (!isDouble) if (!isDouble)
result.KasPersons.Add(person); result.KasPersons.Add(person);
else else
@@ -107,13 +156,25 @@ public class CombineAddresses
if (return_unused == true) return (result, second_result); if (return_unused == true) return (result, second_result);
return (result, null); return (result, null);
} }
catch (Exception ex)
{
Logger.Log($"Error while performing difference-combining: {ex.Message}", Logger.LogType.Error);
}
return (null,null);
}
public async Task<(KasAddressList, KasAddressList)> Union(List<KasAddressList> address_lists, bool? return_unused, public async Task<(KasAddressList, KasAddressList)> Union(List<KasAddressList> address_lists, CombineType comb_type, bool? return_unused,
Progress progress = null) Progress progress = null)
{
try
{ {
var result = new KasAddressList(await KasAddressList.GenerateName("union")); var result = new KasAddressList(await KasAddressList.GenerateName("union"));
var second_result = new KasAddressList(await KasAddressList.GenerateName("union_rest", false)); var second_result = new KasAddressList("none");
if(return_unused == true)
second_result = new KasAddressList(await KasAddressList.GenerateName("union_rest", false));
if (address_lists == null || address_lists.Count == 0) if (address_lists == null || address_lists.Count == 0)
return (result, null); return (result, null);
@@ -124,7 +185,7 @@ public class CombineAddresses
foreach (var list in address_lists) foreach (var list in address_lists)
foreach (var person in list.KasPersons) foreach (var person in list.KasPersons)
{ {
if (!result.KasPersons.Any(existing => CompareAddresses(existing, person))) if (!result.KasPersons.Any(existing => CompareAddresses(existing, person, comb_type)))
result.KasPersons.Add(person); result.KasPersons.Add(person);
else else
second_result.KasPersons.Add(person); second_result.KasPersons.Add(person);
@@ -144,6 +205,14 @@ public class CombineAddresses
if (return_unused == true) return (result, second_result); if (return_unused == true) return (result, second_result);
return (result, null); return (result, null);
} }
catch (Exception ex)
{
Logger.Log($"Error while performing union-combining: {ex.Message}", Logger.LogType.Error);
}
return (null,null);
}
public async Task<KasAddressList> MoveDuplicatesToNew() public async Task<KasAddressList> MoveDuplicatesToNew()
@@ -151,11 +220,14 @@ public class CombineAddresses
return null; return null;
} }
public async Task<(KasAddressList, KasAddressList)> Intersection(List<KasAddressList> address_lists, public async Task<(KasAddressList, KasAddressList)> Intersection(List<KasAddressList> address_lists, CombineType comb_type,
bool? return_unused, Progress progress = null) bool? return_unused, Progress progress = null)
{
try
{ {
var result = new KasAddressList(await KasAddressList.GenerateName("intersection")); var result = new KasAddressList(await KasAddressList.GenerateName("intersection"));
var second_result = new KasAddressList(await KasAddressList.GenerateName("intersection_rest", false)); var second_result = new KasAddressList("none");
if(return_unused == true) second_result = new KasAddressList(await KasAddressList.GenerateName("intersection_rest", false));
if (address_lists == null || address_lists.Count == 0) if (address_lists == null || address_lists.Count == 0)
return (result, null); return (result, null);
@@ -171,7 +243,7 @@ public class CombineAddresses
{ {
// Prüfen, ob Person in *allen* anderen Listen vorkommt // Prüfen, ob Person in *allen* anderen Listen vorkommt
var isInAll = otherLists.All(list => var isInAll = otherLists.All(list =>
list.KasPersons.Any(existing => CompareAddresses(existing, person))); list.KasPersons.Any(existing => CompareAddresses(existing, person, comb_type)));
if (isInAll) if (isInAll)
result.KasPersons.Add(person); result.KasPersons.Add(person);
@@ -196,13 +268,24 @@ public class CombineAddresses
if (return_unused == true) return (result, second_result); if (return_unused == true) return (result, second_result);
return (result, null); return (result, null);
} }
catch (Exception ex)
{
Logger.Log($"Error while performing intersection-combining: {ex.Message}", Logger.LogType.Error);
}
return (null,null);
}
public async Task<(KasAddressList, KasAddressList)> SymmetricDifference(List<KasAddressList> address_lists, public async Task<(KasAddressList, KasAddressList)> SymmetricDifference(List<KasAddressList> address_lists, CombineType comb_type,
bool? return_unused, Progress progress = null) bool? return_unused, Progress progress = null)
{
try
{ {
var result = new KasAddressList(await KasAddressList.GenerateName("symmetric_difference")); var result = new KasAddressList(await KasAddressList.GenerateName("symmetric_difference"));
var second_result = new KasAddressList(await KasAddressList.GenerateName("symmetric_rest", false)); var second_result = new KasAddressList("none");
if(return_unused == true) second_result = new KasAddressList(await KasAddressList.GenerateName("symmetric_rest", false));
if (address_lists == null || address_lists.Count == 0) if (address_lists == null || address_lists.Count == 0)
return (result, null); return (result, null);
@@ -217,7 +300,7 @@ public class CombineAddresses
foreach (var person in list.KasPersons) foreach (var person in list.KasPersons)
{ {
// Prüfen, ob schon vorhanden // Prüfen, ob schon vorhanden
var existing = allPersons.FirstOrDefault(p => CompareAddresses(p.person, person)); var existing = allPersons.FirstOrDefault(p => CompareAddresses(p.person, person, comb_type));
if (existing.person != null) if (existing.person != null)
{ {
// Falls schon drin → Vorkommen erhöhen // Falls schon drin → Vorkommen erhöhen
@@ -254,66 +337,15 @@ public class CombineAddresses
if (return_unused == true) return (result, second_result); if (return_unused == true) return (result, second_result);
return (result, null); return (result, null);
} }
catch (Exception ex)
{
Logger.Log($"Error while performing symdiff-combining: {ex.Message}", Logger.LogType.Error);
}
return (null,null);
}
// private async Task<KasAddressList> Merge(KasAddressList first, KasAddressList second, int num, int total)
// {
// foreach (var sec in second.KasPersons)
// {
// var is_new = true;
// foreach (var fi in first.KasPersons)
// {
// if (fi.refsid == sec.refsid)
// {
// is_new = false;
// break;
// }
//
// if (fi.name == sec.name &&
// fi.anrede == sec.anrede &&
// fi.anredzus == sec.anredzus &&
// fi.namezus == sec.namezus &&
// fi.titel == sec.titel &&
// fi.adel == sec.adel &&
// fi.strasse == sec.strasse &&
// fi.strasse2 == sec.strasse2 &&
// fi.vorname == sec.vorname &&
// fi.ort == sec.ort &&
// fi.land == sec.land &&
// fi.plz == sec.plz &&
// fi.pplz == sec.pplz &&
// fi.funktion == sec.funktion &&
// fi.funktion2 == sec.funktion2 &&
// fi.funktionad == sec.funktionad &&
// fi.abteilung == sec.abteilung &&
// fi.postfach == sec.postfach &&
// fi.name1 == sec.name1 &&
// fi.name2 == sec.name2 &&
// fi.name3 == sec.name3 &&
// fi.name4 == sec.name4 &&
// fi.name5 == sec.name5)
// {
// is_new = false;
// break;
// }
// }
//
// if (is_new) first.KasPersons.Add(sec);
// var subperc = second.KasPersons.IndexOf(sec) / second.KasPersons.Count;
// var percent = (num + (double)subperc) / total * 100;
// await Dispatcher.UIThread.InvokeAsync(() =>
// {
// if (is_new)
// _progress.AddToLog($"Person mit refsid {sec.refsid} ergänzt");
// else
// _progress.AddToLog($"Person mit refsid {sec.refsid} bereits vorhanden");
//
// _progress.ChangePercentage(percent);
// });
// }
//
// return first;
// }
} }
public class Progress public class Progress
+11
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -64,6 +65,8 @@ public class CsvBuilder
} }
private string EscapeCsvField(string? value) private string EscapeCsvField(string? value)
{
try
{ {
var field = value ?? string.Empty; var field = value ?? string.Empty;
var mustQuote = field.Contains(Separator) || field.Contains('"') || field.Contains('\r') || field.Contains('\n'); var mustQuote = field.Contains(Separator) || field.Contains('"') || field.Contains('\r') || field.Contains('\n');
@@ -72,4 +75,12 @@ public class CsvBuilder
return "\"" + field.Replace("\"", "\"\"") + "\""; return "\"" + field.Replace("\"", "\"\"") + "\"";
} }
catch (Exception ex)
{
Logger.Log($"Error while escapting csv field: {ex.Message}",Logger.LogType.Warning);
}
return "";
}
} }
+29 -2
View File
@@ -19,6 +19,8 @@ public class DataImport
} }
private static async Task<(bool, KasAddressList)> ImportKasAddressListWithoutPatch(Uri pathToCsv, char separator) private static async Task<(bool, KasAddressList)> ImportKasAddressListWithoutPatch(Uri pathToCsv, char separator)
{
try
{ {
if (!File.Exists(pathToCsv.LocalPath)) if (!File.Exists(pathToCsv.LocalPath))
{ {
@@ -83,7 +85,7 @@ public class DataImport
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Error while parsing line: {line} - {ex.Message}"); Logger.Log($"Error while creating new kas person (import): {ex.Message}",Logger.LogType.Error);
Console.WriteLine(ex.StackTrace); Console.WriteLine(ex.StackTrace);
return (false, null); return (false, null);
} }
@@ -91,9 +93,18 @@ public class DataImport
return (true, imported); return (true, imported);
} }
catch (Exception ex)
{
Logger.Log($"Error while importing kas address list without patch: {ex.Message}",Logger.LogType.Error);
}
return (false, null);
}
private static async Task<(bool, KasAddressList)> ImportKasAddressListWithPatch(Uri pathToCsv, AddressPatch patch, private static async Task<(bool, KasAddressList)> ImportKasAddressListWithPatch(Uri pathToCsv, AddressPatch patch,
char separator) char separator)
{
try
{ {
if (!File.Exists(pathToCsv.LocalPath)) if (!File.Exists(pathToCsv.LocalPath))
{ {
@@ -217,13 +228,19 @@ public class DataImport
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Error while parsing line: {line} - {ex.Message}"); Logger.Log($"Error while creating kas person (import, patch): {ex.Message}",Logger.LogType.Error);
Console.WriteLine(ex.StackTrace); Console.WriteLine(ex.StackTrace);
return (false, null); return (false, null);
} }
} }
return (true, imported); return (true, imported);
}
catch (Exception ex)
{
Logger.Log($"Error while importing kas address list with patch: {ex.Message}",Logger.LogType.Error);
}
// int GenerateNewRefsid() // int GenerateNewRefsid()
// { // {
@@ -236,6 +253,7 @@ public class DataImport
// last_refsid = biggest + 1; // last_refsid = biggest + 1;
// return last_refsid; // return last_refsid;
// } // }
return (false, null);
} }
@@ -245,6 +263,8 @@ public class DataImport
} }
private static string[] ParseCsvLine(string line, char separator) private static string[] ParseCsvLine(string line, char separator)
{
try
{ {
var fields = new List<string>(); var fields = new List<string>();
var current = new StringBuilder(); var current = new StringBuilder();
@@ -280,4 +300,11 @@ public class DataImport
fields.Add(current.ToString().Trim()); fields.Add(current.ToString().Trim());
return fields.ToArray(); return fields.ToArray();
} }
catch (Exception ex)
{
Logger.Log($"Error while persing csv line: {ex.Message}",Logger.LogType.Error);
}
return [];
}
} }
+94 -1
View File
@@ -17,6 +17,8 @@ public class PdfBuilder
private readonly XFont _smallFont; private readonly XFont _smallFont;
public PdfBuilder(PdfExportSettings? settings = null) public PdfBuilder(PdfExportSettings? settings = null)
{
try
{ {
EnsureFontResolverRegistered(); EnsureFontResolverRegistered();
_settings = settings ?? new PdfExportSettings(); _settings = settings ?? new PdfExportSettings();
@@ -32,8 +34,9 @@ public class PdfBuilder
chosenFamily = StripStyleSuffix(Path.GetFileNameWithoutExtension(first)) ?? chosenFamily; chosenFamily = StripStyleSuffix(Path.GetFileNameWithoutExtension(first)) ?? chosenFamily;
} }
} }
catch catch (Exception ex)
{ {
Logger.Log($"Error while searching for fonts: {ex.Message}",Logger.LogType.Error);
chosenFamily = "Arial"; chosenFamily = "Arial";
} }
@@ -41,15 +44,32 @@ public class PdfBuilder
_regularFont = new XFont(chosenFamily, _settings.fontSize, XFontStyleEx.Regular); _regularFont = new XFont(chosenFamily, _settings.fontSize, XFontStyleEx.Regular);
_smallFont = new XFont(chosenFamily, _settings.smallFontSize, XFontStyleEx.Regular); _smallFont = new XFont(chosenFamily, _settings.smallFontSize, XFontStyleEx.Regular);
} }
catch (Exception ex)
{
Logger.Log($"Error while font resolving: {ex.Message}",Logger.LogType.Error);
}
}
private static void EnsureFontResolverRegistered() private static void EnsureFontResolverRegistered()
{
try
{ {
if (GlobalFontSettings.FontResolver != null) return; if (GlobalFontSettings.FontResolver != null) return;
//var fontsDir = Path.Combine(AppContext.BaseDirectory, "fonts"); //var fontsDir = Path.Combine(AppContext.BaseDirectory, "fonts");
GlobalFontSettings.FontResolver = new StableFontResolver(Global._instance.font_path); GlobalFontSettings.FontResolver = new StableFontResolver(Global._instance.font_path);
} }
catch (Exception ex)
{
Logger.Log($"Error while ensuring font resolver register state: {ex.Message}",Logger.LogType.Error);
}
}
private static string StripStyleSuffix(string name) private static string StripStyleSuffix(string name)
{
try
{ {
if (string.IsNullOrEmpty(name)) return name; if (string.IsNullOrEmpty(name)) return name;
var idx = name.IndexOf('-'); var idx = name.IndexOf('-');
@@ -58,6 +78,14 @@ public class PdfBuilder
return name.Substring(0, idx); return name.Substring(0, idx);
return name; return name;
} }
catch (Exception ex)
{
Logger.Log($"Error while stripping style suffix: {ex.Message}",Logger.LogType.Error);
}
return null;
}
/// <summary> /// <summary>
@@ -68,6 +96,8 @@ public class PdfBuilder
/// <param name="outputPath">Path where the PDF should be saved</param> /// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdfFromAddressSetWithPlaceholder(int addressSetId, string placeholderText, public void CreateAddressLabelPdfFromAddressSetWithPlaceholder(int addressSetId, string placeholderText,
string outputPath) string outputPath)
{
try
{ {
// Find the AddressSet by ID // Find the AddressSet by ID
var addressSet = Settings._instance.addressSets.GetAddressSetByID(addressSetId); var addressSet = Settings._instance.addressSets.GetAddressSetByID(addressSetId);
@@ -106,6 +136,12 @@ public class PdfBuilder
CreateAddressLabelPdfWithPlaceholder(addresses, placeholderText, outputPath); CreateAddressLabelPdfWithPlaceholder(addresses, placeholderText, outputPath);
} }
catch (Exception ex)
{
Logger.Log($"Error while creating address label pdf from address set with placeholder: {ex.Message}",Logger.LogType.Error);
}
}
/// <summary> /// <summary>
/// Creates a PDF document with a single placeholder cell for other information. /// Creates a PDF document with a single placeholder cell for other information.
@@ -114,6 +150,8 @@ public class PdfBuilder
/// <param name="placeholderText">Text for the first cell (top-left)</param> /// <param name="placeholderText">Text for the first cell (top-left)</param>
/// <param name="outputPath">Path where the PDF should be saved</param> /// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdfWithPlaceholder(List<string> addresses, string placeholderText, string outputPath) public void CreateAddressLabelPdfWithPlaceholder(List<string> addresses, string placeholderText, string outputPath)
{
try
{ {
if (addresses == null || addresses.Count == 0) if (addresses == null || addresses.Count == 0)
throw new ArgumentException("Addresses array cannot be null or empty"); throw new ArgumentException("Addresses array cannot be null or empty");
@@ -136,6 +174,12 @@ public class PdfBuilder
document.Save(outputPath); document.Save(outputPath);
} }
catch (Exception ex)
{
Logger.Log($"Error while creating address label pdf with placeholder: {ex.Message}",Logger.LogType.Error);
}
}
private void DrawPage(XGraphics gfx, List<string> addresses, ref int addressIndex) private void DrawPage(XGraphics gfx, List<string> addresses, ref int addressIndex)
{ {
@@ -158,6 +202,8 @@ public class PdfBuilder
private void DrawPageWithPlaceholder(XGraphics gfx, List<string> addresses, ref int addressIndex, private void DrawPageWithPlaceholder(XGraphics gfx, List<string> addresses, ref int addressIndex,
ref bool isFirstCell, string placeholderText) ref bool isFirstCell, string placeholderText)
{
try
{ {
for (var row = 0; row < _settings.rowsPerPage; row++) for (var row = 0; row < _settings.rowsPerPage; row++)
for (var col = 0; col < _settings.columnsPerPage; col++) for (var col = 0; col < _settings.columnsPerPage; col++)
@@ -182,8 +228,16 @@ public class PdfBuilder
} }
} }
} }
catch (Exception ex)
{
Logger.Log($"Error while drawing page with placholder: {ex.Message}",Logger.LogType.Error);
}
}
private void DrawCell(XGraphics gfx, double x, double y, string? address) private void DrawCell(XGraphics gfx, double x, double y, string? address)
{
try
{ {
var cellWidthPoints = MmToPoints(GetCellWidthMm()); var cellWidthPoints = MmToPoints(GetCellWidthMm());
var cellHeightPoints = MmToPoints(GetCellHeightMm()); var cellHeightPoints = MmToPoints(GetCellHeightMm());
@@ -195,8 +249,16 @@ public class PdfBuilder
// Draw address content if available // Draw address content if available
if (!string.IsNullOrEmpty(address)) DrawMarkdownText(gfx, address, x, y, cellWidthPoints, cellHeightPoints); if (!string.IsNullOrEmpty(address)) DrawMarkdownText(gfx, address, x, y, cellWidthPoints, cellHeightPoints);
} }
catch (Exception ex)
{
Logger.Log($"Error while drawing cell: {ex.Message}",Logger.LogType.Error);
}
}
private void DrawEmptyCell(XGraphics gfx, double x, double y) private void DrawEmptyCell(XGraphics gfx, double x, double y)
{
try
{ {
var cellWidthPoints = MmToPoints(GetCellWidthMm()); var cellWidthPoints = MmToPoints(GetCellWidthMm());
var cellHeightPoints = MmToPoints(GetCellHeightMm()); var cellHeightPoints = MmToPoints(GetCellHeightMm());
@@ -204,8 +266,16 @@ public class PdfBuilder
var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints); var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints);
gfx.DrawRectangle(XPens.Black, rect); gfx.DrawRectangle(XPens.Black, rect);
} }
catch (Exception ex)
{
Logger.Log($"Error while drawing empty cell: {ex.Message}",Logger.LogType.Error);
}
}
private void DrawMarkdownText(XGraphics gfx, string text, double x, double y, double cellWidth, double cellHeight) private void DrawMarkdownText(XGraphics gfx, string text, double x, double y, double cellWidth, double cellHeight)
{
try
{ {
var paddingLeftPoints = MmToPoints(_settings.cellPaddingLeftMm); var paddingLeftPoints = MmToPoints(_settings.cellPaddingLeftMm);
var paddingRightPoints = MmToPoints(_settings.cellPaddingRightMm); var paddingRightPoints = MmToPoints(_settings.cellPaddingRightMm);
@@ -240,8 +310,16 @@ public class PdfBuilder
currentY += lineHeight; currentY += lineHeight;
} }
} }
catch (Exception ex)
{
Logger.Log($"Error while drawing markdown text: {ex.Message}",Logger.LogType.Error);
}
}
private void DrawLineWithMarkdown(XGraphics gfx, string line, double x, double y, double maxWidth) private void DrawLineWithMarkdown(XGraphics gfx, string line, double x, double y, double maxWidth)
{
try
{ {
if (string.IsNullOrWhiteSpace(line)) return; if (string.IsNullOrWhiteSpace(line)) return;
var currentX = x; var currentX = x;
@@ -331,8 +409,16 @@ public class PdfBuilder
i = textEnd; i = textEnd;
} }
} }
catch (Exception ex)
{
Logger.Log($"Error while drawing markdown line: {ex.Message}",Logger.LogType.Error);
}
}
private string TruncateTextToWidth(XGraphics gfx, string text, XFont font, double maxWidth) private string TruncateTextToWidth(XGraphics gfx, string text, XFont font, double maxWidth)
{
try
{ {
if (string.IsNullOrEmpty(text)) if (string.IsNullOrEmpty(text))
return text; return text;
@@ -347,6 +433,13 @@ public class PdfBuilder
return string.Empty; return string.Empty;
} }
catch (Exception ex)
{
Logger.Log($"Error while truncating text to width: {ex.Message}",Logger.LogType.Error);
}
return null;
}
/// <summary> /// <summary>
/// Converts millimeters to points (1 mm = 2.834645669 points) /// Converts millimeters to points (1 mm = 2.834645669 points)
+29 -2
View File
@@ -62,13 +62,15 @@ public class StableFontResolver : IFontResolver
} }
} }
} }
catch catch (Exception ex)
{ {
// ignore resolver init errors Logger.Log($"Error while initializing FontResolver: {ex.Message}",Logger.LogType.Error);
} }
} }
private static string NormalizeStyle(string s) private static string NormalizeStyle(string s)
{
try
{ {
if (string.IsNullOrEmpty(s)) return "Regular"; if (string.IsNullOrEmpty(s)) return "Regular";
s = s.ToLowerInvariant(); s = s.ToLowerInvariant();
@@ -78,8 +80,17 @@ public class StableFontResolver : IFontResolver
if (s.Contains("regular") || s == "r") return "Regular"; if (s.Contains("regular") || s == "r") return "Regular";
return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(s); return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(s);
} }
catch (Exception ex)
{
Logger.Log($"Error while normalizing style: {ex.Message}",Logger.LogType.Error);
}
return null;
}
public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic) public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic)
{
try
{ {
// If requested family exists, pick corresponding style if available // If requested family exists, pick corresponding style if available
string familyToUse = null; string familyToUse = null;
@@ -100,8 +111,17 @@ public class StableFontResolver : IFontResolver
// Face name format: Family#Style // Face name format: Family#Style
return new FontResolverInfo($"{familyToUse}#{style}"); return new FontResolverInfo($"{familyToUse}#{style}");
} }
catch (Exception ex)
{
Logger.Log($"Error while resolving typeface: {ex.Message}",Logger.LogType.Error);
}
return null;
}
public byte[] GetFont(string faceName) public byte[] GetFont(string faceName)
{
try
{ {
if (string.IsNullOrEmpty(faceName)) return null; if (string.IsNullOrEmpty(faceName)) return null;
@@ -133,4 +153,11 @@ public class StableFontResolver : IFontResolver
return null; return null;
} }
catch (Exception ex)
{
Logger.Log($"Error while getting font: {ex.Message}",Logger.LogType.Error);
}
return [];
}
} }
+12 -6
View File
@@ -10,20 +10,24 @@ public partial class EditorWindow : Window
public string filename = ""; public string filename = "";
public EditorWindow(string filename = "") public EditorWindow(string filename = "")
{
try
{ {
InitializeComponent(); InitializeComponent();
this.filename = filename; this.filename = PathUtilities.NormalizeFileSystemPath(filename);
if (!string.IsNullOrWhiteSpace(filename) && File.Exists(filename)) if (!string.IsNullOrWhiteSpace(this.filename) && File.Exists(this.filename))
{ {
var content = TbContent; var content = TbContent;
if (content != null) content.Text = File.ReadAllText(this.filename); if (content != null) content.Text = File.ReadAllText(this.filename);
Title = "Wiki Editor - " + filename; Title = "Wiki Editor - " + filename;
} }
else if (!string.IsNullOrWhiteSpace(filename)) else if (!string.IsNullOrWhiteSpace(this.filename))
{ {
MessageBox.Show(null, "Die Datei existiert nicht", "Fehler"); MessageBox.Show(null, "Die Datei existiert nicht", "Fehler");
Close(); Close();
} }
} catch (Exception ex) { Logger.Log($"Error while : {ex.Message}",Logger.LogType.Error);}
} }
private void BtnSave_OnClick(object? sender, RoutedEventArgs e) private void BtnSave_OnClick(object? sender, RoutedEventArgs e)
@@ -31,20 +35,22 @@ public partial class EditorWindow : Window
try try
{ {
File.WriteAllText(filename, TbContent.Text); File.WriteAllText(filename, TbContent.Text);
MainWindow._instance.PopulateNavTree(); MainWindow._instance.PopulateNavTree(filename, filename);
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.Show(null, MessageBox.Show(null,
"Es ist ein Fehler aufgetreten. Bitte senden Sie ihn über git.mypapercloud.de/fierke/logofclient ein:\n" + "Es ist ein Fehler aufgetreten. Bitte senden Sie ihn über git.mypapercloud.de/fierke/logofclient ein:\n" +
ex.StackTrace, "Fehler"); ex.StackTrace, "Fehler");
Logger.Log($"Error while : {ex.Message}",Logger.LogType.Error);
} }
} }
private void BtnSaveAs_OnClick(object? sender, RoutedEventArgs e) private void BtnSaveAs_OnClick(object? sender, RoutedEventArgs e)
{ {
MessageBox.Show(null, MessageBox.Show(null,
"Feature noch nicht implemetiert.\nErstelle neue Dateien unter " + Global._instance.wiki_storage_path, "Feature noch nicht implemetiert.\nErstelle neue Dateien unter " +
PathUtilities.NormalizeFileSystemPath(Global._instance.wiki_storage_path),
"Fehler"); "Fehler");
} }
@@ -55,7 +61,7 @@ public partial class EditorWindow : Window
try try
{ {
File.Delete(filename); File.Delete(filename);
} catch {} } catch (Exception ex) { Logger.Log($"Error while : {ex.Message}",Logger.LogType.Error);}
MainWindow._instance.PopulateNavTree(); MainWindow._instance.PopulateNavTree();
Close(); Close();
+11
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
@@ -11,6 +12,8 @@ namespace Logof_Client.Wiki;
public static class MarkdownRenderer public static class MarkdownRenderer
{ {
public static Control Render(string markdown) public static Control Render(string markdown)
{
try
{ {
var panel = new StackPanel { Spacing = 6 }; var panel = new StackPanel { Spacing = 6 };
if (string.IsNullOrWhiteSpace(markdown)) return panel; if (string.IsNullOrWhiteSpace(markdown)) return panel;
@@ -93,9 +96,14 @@ public static class MarkdownRenderer
} }
return panel; return panel;
} catch (Exception ex) { Logger.Log($"Error while : {ex.Message}",Logger.LogType.Error);}
return new Panel();
} }
private static string GetInlineText(ContainerInline? container) private static string GetInlineText(ContainerInline? container)
{
try
{ {
if (container == null) return string.Empty; if (container == null) return string.Empty;
var sb = new StringBuilder(); var sb = new StringBuilder();
@@ -125,5 +133,8 @@ public static class MarkdownRenderer
} }
return sb.ToString(); return sb.ToString();
} catch (Exception ex) { Logger.Log($"Error while : {ex.Message}",Logger.LogType.Error);}
return null;
} }
} }
+5 -2
View File
@@ -13,7 +13,7 @@ public class WikiService
// prefer global wiki storage path if configured // prefer global wiki storage path if configured
if (Global._instance != null && !string.IsNullOrWhiteSpace(Global._instance.wiki_storage_path)) if (Global._instance != null && !string.IsNullOrWhiteSpace(Global._instance.wiki_storage_path))
{ {
var cfg = Global._instance.wiki_storage_path; var cfg = PathUtilities.NormalizeFileSystemPath(Global._instance.wiki_storage_path);
WikiRootFullPath = Path.IsPathRooted(cfg) WikiRootFullPath = Path.IsPathRooted(cfg)
? cfg ? cfg
: Path.Combine(Directory.GetCurrentDirectory(), cfg); : Path.Combine(Directory.GetCurrentDirectory(), cfg);
@@ -25,8 +25,11 @@ public class WikiService
wikiRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), wikiRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"logofclient", "logofclient",
"wiki"); "wiki");
wikiRoot = PathUtilities.NormalizeFileSystemPath(wikiRoot);
WikiRoot = wikiRoot; WikiRoot = wikiRoot;
WikiRootFullPath = Path.Combine(Directory.GetCurrentDirectory(), wikiRoot); WikiRootFullPath = Path.IsPathRooted(wikiRoot)
? wikiRoot
: Path.Combine(Directory.GetCurrentDirectory(), wikiRoot);
} }
} }
-157
View File
@@ -1,157 +0,0 @@
\documentclass[a4paper]{article}
\usepackage{graphicx}
\usepackage[left=3cm,right=3cm,top=3cm, bottom=3cm]{geometry}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage[headsepline, footsepline]{scrlayer-scrpage}
\usepackage{enumerate}
\usepackage{dsfont}
\usepackage[]{mathtools}
\usepackage[]{mathbbol}
\usepackage{multicol}
\usepackage{enumitem}
\usepackage[hidelinks]{hyperref}
\usepackage[]{circuitikz}
\usepackage{tcolorbox} % Für schöne Boxen
\usetikzlibrary{circuits.logic.IEC}
\usepackage{tikz}
\usepackage{tkz-graph}
\usepackage{listings}
\usepackage{xcolor}
\setlist{itemsep=0.3em, topsep=0.5em, parsep=0pt}
\newcommand{\bpf}[1]{%
\par\vspace{0.8\baselineskip}% Abstand vor der Überschrift
\noindent\textbf{#1}% Fettgedruckte Überschrift
\par\vspace{0.3\baselineskip}% Abstand nach der Überschrift
}
\renewcommand{\contentsname}{Inhaltsverzeichnis}
\renewcommand{\figurename}{Grafik}
\renewcommand{\partname}{Teil}
\renewcommand{\epsilon}{\varepsilon}
\definecolor{darkgrey}{HTML}{232327}
% Zählerdefinition mit AUTOMATISCHEM SUBSECTION-RESET
\newcounter{commoncounter}[subsection]
\renewcommand{\thecommoncounter}{\thesubsection.\arabic{commoncounter}}
% Zähler direkt bei Dokumentstart initialisieren
\AtBeginDocument{\setcounter{commoncounter}{0}}
% BOX-DEFINITIONEN mit korrigierter Zählerlogik
\newtcolorbox{definitionbox}[1][]{
before title={\refstepcounter{commoncounter}}, % KRITISCH: vor dem Titel!
title={Definition \thecommoncounter: #1},
colback=white,
colframe=white!75!darkgrey,
fonttitle=\bfseries,
boxrule=0.6mm,
coltitle=black,
rounded corners,
before skip=10pt,
after skip=10pt
}
\newtcolorbox{examplebox}[1][]{
before title={\refstepcounter{commoncounter}}, % KRITISCH: vor dem Titel!
title={Beispiel \thecommoncounter: #1},
colback=white,
colframe=white!75!orange,
fonttitle=\bfseries,
boxrule=0.6mm,
coltitle=black,
rounded corners,
before skip=10pt,
after skip=10pt
}
\newtcolorbox{satzbox}[1][]{
before title={\refstepcounter{commoncounter}}, % KRITISCH: vor dem Titel!
title={Satz \thecommoncounter: #1},
colback=white,
colframe=white!75!blue,
fonttitle=\bfseries,
boxrule=0.6mm,
coltitle=black,
rounded corners,
before skip=10pt,
after skip=10pt
}
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codeblue}{rgb}{0,0,0.8}
\definecolor{codered}{rgb}{0.8,0,0}
\definecolor{lightgray}{rgb}{0.95,0.95,0.95}
\lstdefinestyle{CSharpStyle}{
language=Python,
basicstyle=\ttfamily\small, % Monospace-Schrift
keywordstyle=\color{blue}\bfseries, % Schlüsselwörter fett und blau
stringstyle=\color{red}, % Strings rot
commentstyle=\color{codegreen}, % Kommentare grün
backgroundcolor=\color{lightgray}, % Hintergrundfarbe
numbers=left, % Zeilennummern links
numbersep=10px, % Abstand zwischen Zeilennummern und Code
numberstyle=\color{gray}\texttt,
stepnumber=1, % Zeilennummerierung Schrittweite 1
frame=single, % Rahmen um den Code
tabsize=4, % Tabulatorgröße
breaklines=true, % Zeilenumbruch aktivieren
captionpos=none,
showstringspaces=false,
xleftmargin=15pt, % Linker Rand für den Code (verschiebt alles nach rechts)
}
% Umgebung für Listings mit Titel und Zähler
\newenvironment{codeexample}[1][]{
\refstepcounter{commoncounter} % Zähler erhöhen
\lstset{
style=CSharpStyle,
caption={Listing \thecommoncounter: #1}, % Titel mit Zähler
label={listing:\thecommoncounter}
}
}{}
\setlength{\parindent}{0pt}
\title{\includegraphics[width=0.3\textwidth]{../assets/icon.png}\vspace{15pt}\\Logof Client\\
Handbuch}
\author{Elias Fierke}
\pagestyle{scrheadings}
\date{Oktober 2025}
\begin{document}
\ohead{Oktober 2025}
\ofoot{Seite {\pagemark} von \pageref{LastPage}}
\ihead{Logof Client Handbuch}
\maketitle
\tableofcontents
\newpage
\part{Einführung}
\newpage
\part{Installation}
\newpage
\part{Adress-Verwaltung}
\label{LastPage}
\end{document}