From 2ee0d56dcc010b06a5a89dbc9f034db46642c0d0 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Wed, 13 Nov 2024 18:28:02 +0100 Subject: [PATCH] Windows: Add MailLogWindow --- Elwig/App.xaml.cs | 2 + Elwig/Helpers/ControlUtils.cs | 17 + Elwig/Helpers/Utils.cs | 71 +++ Elwig/Windows/AdministrationWindow.cs | 17 - Elwig/Windows/AreaComAdminWindow.xaml.cs | 2 +- Elwig/Windows/DeliveryAdminWindow.xaml.cs | 2 +- .../Windows/DeliveryAncmtAdminWindow.xaml.cs | 2 +- .../DeliveryScheduleAdminWindow.xaml.cs | 2 +- Elwig/Windows/LogWindow.xaml.cs | 5 +- Elwig/Windows/MailLogWindow.xaml | 54 ++ Elwig/Windows/MailLogWindow.xaml.cs | 83 +++ Elwig/Windows/MailWindow.xaml | 539 ++++++++++-------- Elwig/Windows/MailWindow.xaml.cs | 37 +- Elwig/Windows/MemberAdminWindow.xaml.cs | 2 +- 14 files changed, 565 insertions(+), 270 deletions(-) create mode 100644 Elwig/Windows/MailLogWindow.xaml create mode 100644 Elwig/Windows/MailLogWindow.xaml.cs diff --git a/Elwig/App.xaml.cs b/Elwig/App.xaml.cs index 2c8bc6f..19f7a7a 100644 --- a/Elwig/App.xaml.cs +++ b/Elwig/App.xaml.cs @@ -26,6 +26,7 @@ namespace Elwig { private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) }; public static readonly string DataPath = @"C:\ProgramData\Elwig\"; + public static readonly string MailsPath = Path.Combine(DataPath, "mails"); public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini"); public static readonly string ExePath = @"C:\Program Files\Elwig\"; public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig"); @@ -56,6 +57,7 @@ namespace Elwig { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Directory.CreateDirectory(TempPath); Directory.CreateDirectory(DataPath); + Directory.CreateDirectory(MailsPath); MainDispatcher = Dispatcher; Scales = []; CurrentApp = this; diff --git a/Elwig/Helpers/ControlUtils.cs b/Elwig/Helpers/ControlUtils.cs index 03d9443..47b5203 100644 --- a/Elwig/Helpers/ControlUtils.cs +++ b/Elwig/Helpers/ControlUtils.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; +using System.Windows.Threading; using Brush = System.Windows.Media.Brush; using Brushes = System.Windows.Media.Brushes; @@ -234,5 +235,21 @@ namespace Elwig.Helpers { return null; } } + + public static void InitializeDelayTimer(TextBox tb, Action handler) { + var timer = new DispatcherTimer { + Interval = TimeSpan.FromMilliseconds(250) + }; + timer.Tick += (object? sender, EventArgs evt) => { + timer.Stop(); + var (oSender, oEvent) = ((object, TextChangedEventArgs))timer.Tag; + handler(oSender, oEvent); + }; + tb.TextChanged += (object sender, TextChangedEventArgs evt) => { + timer.Stop(); + timer.Tag = (sender, evt); + timer.Start(); + }; + } } } diff --git a/Elwig/Helpers/Utils.cs b/Elwig/Helpers/Utils.cs index 1de6c6f..ac965a2 100644 --- a/Elwig/Helpers/Utils.cs +++ b/Elwig/Helpers/Utils.cs @@ -594,5 +594,76 @@ namespace Elwig.Helpers { public static IEnumerable ActiveAreaCommitments(IEnumerable query) => ActiveAreaCommitments(query, CurrentYear); public static IEnumerable ActiveAreaCommitments(IEnumerable query, int year) => query.Where(c => ActiveAreaCommitments(year).Invoke(c)); + + public static async Task<(DateTime DateTime, string Type, int MgNr, string Name, string[] Addresses, string Subject, string[] Attachments)[]> GetSentMails(DateOnly? fromDate = null, DateOnly? toDate = null) { + var f = $"{fromDate:yyyy-MM-dd}"; + var t = $"{toDate:yyyy-MM-dd}"; + try { + var rows = new List<(DateTime, string, int, string, string[], string, string[])>(); + var filenames = Directory.GetFiles(App.MailsPath, "????.csv") + .Where(n => fromDate == null || Path.GetFileName(n).CompareTo($"{fromDate.Value.Year}.csv") >= 0) + .Where(n => toDate == null || Path.GetFileName(n).CompareTo($"{toDate.Value.Year}.csv") <= 0) + .Order(); + foreach (var filename in filenames) { + using var reader = new StreamReader(filename, Utils.UTF8); + string? line; + while ((line = await reader.ReadLineAsync()) != null) { + try { + if (line.Length < 20 || line[10] != ';' || line[19] != ';') + continue; + var date = line[..10]; + if (fromDate != null && date.CompareTo(f) < 0) { + continue; + } else if (toDate != null && date.CompareTo(t) > 0) { + break; + } + var p = line.Split(';'); + rows.Add(( + DateOnly.ParseExact(p[0], "yyyy-MM-dd").ToDateTime(TimeOnly.ParseExact(p[1], "HH:mm:ss")), + p[2], + int.Parse(p[3]), + p[4], + p[5].Split(',').Select(a => a.Replace(" | ", "\n")).ToArray(), + p[6], + p[7].Split(',') + )); + } catch { + continue; + } + } + } + return [.. rows]; + } catch { + return []; + } + } + + public static async Task AddSentMails(IEnumerable<(string Type, int MgNr, string Name, string[] Addresses, string Subject, string[] Attachments)> data) { + var now = DateTime.Now; + var filename = Path.Combine(App.MailsPath, $"{now.Year}.csv"); + await File.AppendAllLinesAsync(filename, data.Select(d => + $"{now:yyyy-MM-dd;HH:mm:ss};{d.Type};" + + $"{d.MgNr};{d.Name.Replace(';', ' ')};" + + $"{string.Join(',', d.Addresses.Select(a => a.Replace(';', ' ').Replace(',', ' ').Replace("\n", " | ")))};" + + $"{d.Subject.Replace(';', ' ')};" + + $"{string.Join(',', d.Attachments.Select(a => a.Replace(';', ' ').Replace(',', ' ')))}" + ), Utils.UTF8); + } + + public static async Task FindSentMailBody(DateTime target) { + var dt = $"{target:yyyy-MM-dd_HH-mm-ss}_"; + var filename = Directory.GetFiles(App.MailsPath, "????-??-??_??-??-??_*.txt") + .Where(n => Path.GetFileName(n).CompareTo(dt) <= 0) + .Order() + .LastOrDefault(); + if (filename == null) + return null; + return await File.ReadAllTextAsync(filename, Utils.UTF8); + } + + public static async Task AddSentMailBody(string subject, string body, int recipients) { + var filename = Path.Combine(App.MailsPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{NormalizeFileName(subject)}.txt"); + await File.WriteAllTextAsync(filename, $"# {subject}\r\n# Vorgesehene Empfänger: {recipients}\r\n\r\n" + body, Utils.UTF8); + } } } diff --git a/Elwig/Windows/AdministrationWindow.cs b/Elwig/Windows/AdministrationWindow.cs index 009d391..e17ba3b 100644 --- a/Elwig/Windows/AdministrationWindow.cs +++ b/Elwig/Windows/AdministrationWindow.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; -using System.Windows.Threading; using System.Windows.Input; namespace Elwig.Windows { @@ -358,22 +357,6 @@ namespace Elwig.Windows { UpdateComboBox(ortInput); } - protected static void InitializeDelayTimer(TextBox tb, Action handler) { - var timer = new DispatcherTimer { - Interval = TimeSpan.FromMilliseconds(250) - }; - timer.Tick += (object? sender, EventArgs evt) => { - timer.Stop(); - var (oSender, oEvent) = ((object, TextChangedEventArgs))timer.Tag; - handler(oSender, oEvent); - }; - tb.TextChanged += (object sender, TextChangedEventArgs evt) => { - timer.Stop(); - timer.Tag = (sender, evt); - timer.Start(); - }; - } - protected bool InputTextChanged(TextBox input) { return InputTextChanged(input, new ValidationResult(true, null)); } diff --git a/Elwig/Windows/AreaComAdminWindow.xaml.cs b/Elwig/Windows/AreaComAdminWindow.xaml.cs index f770d23..2b3c17f 100644 --- a/Elwig/Windows/AreaComAdminWindow.xaml.cs +++ b/Elwig/Windows/AreaComAdminWindow.xaml.cs @@ -33,7 +33,7 @@ namespace Elwig.Windows { GstNrInput, AreaInput, AreaComTypeInput, WineCultivationInput ]; - InitializeDelayTimer(SearchInput, SearchInput_TextChanged); + ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged); SearchInput.TextChanged -= SearchInput_TextChanged; ActiveAreaCommitmentInput.Content = ((string)ActiveAreaCommitmentInput.Content).Replace("2020", $"{Utils.CurrentLastSeason}"); } diff --git a/Elwig/Windows/DeliveryAdminWindow.xaml.cs b/Elwig/Windows/DeliveryAdminWindow.xaml.cs index d1f46d9..073504a 100644 --- a/Elwig/Windows/DeliveryAdminWindow.xaml.cs +++ b/Elwig/Windows/DeliveryAdminWindow.xaml.cs @@ -66,7 +66,7 @@ namespace Elwig.Windows { SecondsTimer.Tick += new EventHandler(OnSecondPassed); SecondsTimer.Interval = new TimeSpan(0, 0, 1); - InitializeDelayTimer(SearchInput, SearchInput_TextChanged); + ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged); SearchInput.TextChanged -= SearchInput_TextChanged; ViewModel.FilterSeason = Utils.CurrentLastSeason; diff --git a/Elwig/Windows/DeliveryAncmtAdminWindow.xaml.cs b/Elwig/Windows/DeliveryAncmtAdminWindow.xaml.cs index fd37bae..c013a97 100644 --- a/Elwig/Windows/DeliveryAncmtAdminWindow.xaml.cs +++ b/Elwig/Windows/DeliveryAncmtAdminWindow.xaml.cs @@ -33,7 +33,7 @@ namespace Elwig.Windows { DoShowWarningWindows = false; - InitializeDelayTimer(SearchInput, SearchInput_TextChanged); + ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged); SearchInput.TextChanged -= SearchInput_TextChanged; ViewModel.FilterSeason = Utils.CurrentLastSeason; } diff --git a/Elwig/Windows/DeliveryScheduleAdminWindow.xaml.cs b/Elwig/Windows/DeliveryScheduleAdminWindow.xaml.cs index e136876..83443d7 100644 --- a/Elwig/Windows/DeliveryScheduleAdminWindow.xaml.cs +++ b/Elwig/Windows/DeliveryScheduleAdminWindow.xaml.cs @@ -27,7 +27,7 @@ namespace Elwig.Windows { DateInput, BranchInput, DescriptionInput, MainWineVarietiesInput, ]; - InitializeDelayTimer(SearchInput, SearchInput_TextChanged); + ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged); SearchInput.TextChanged -= SearchInput_TextChanged; ViewModel.FilterSeason = Utils.CurrentLastSeason; } diff --git a/Elwig/Windows/LogWindow.xaml.cs b/Elwig/Windows/LogWindow.xaml.cs index 0ef0329..4531a1d 100644 --- a/Elwig/Windows/LogWindow.xaml.cs +++ b/Elwig/Windows/LogWindow.xaml.cs @@ -36,9 +36,10 @@ namespace Elwig.Windows { } private void EventList_SelectionChanged(object sender, RoutedEventArgs evt) { - var t = EventList.SelectedItem.GetType(); + var item = EventList.SelectedItem; + var t = item.GetType(); var p = t.GetProperty("Event")!; - EventData.Text = ((EventLogEntry)p.GetValue(EventList.SelectedItem)!).Message; + EventData.Text = ((EventLogEntry)p.GetValue(item)!).Message; } } } diff --git a/Elwig/Windows/MailLogWindow.xaml b/Elwig/Windows/MailLogWindow.xaml new file mode 100644 index 0000000..6db1902 --- /dev/null +++ b/Elwig/Windows/MailLogWindow.xaml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Elwig/Windows/MailLogWindow.xaml.cs b/Elwig/Windows/MailLogWindow.xaml.cs new file mode 100644 index 0000000..31e5a65 --- /dev/null +++ b/Elwig/Windows/MailLogWindow.xaml.cs @@ -0,0 +1,83 @@ +using Elwig.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; + +namespace Elwig.Windows { + public partial class MailLogWindow : Window { + + record struct Row (DateTime DateTime, string Type, int MgNr, string Name, string Addresses, string Subject, string Attachments); + + private List Data = []; + + public MailLogWindow() { + InitializeComponent(); + WindowState = WindowState.Maximized; + ControlUtils.InitializeDelayTimer(FilterInput, FilterInput_TextChanged); + FilterInput.TextChanged -= FilterInput_TextChanged; + } + + private async void TimeSpanInput_SelectionChanged(object sender, RoutedEventArgs evt) { + DateTime? fromDate = DateTime.Now; + if (TimeSpanInput.SelectedIndex == 0) { + fromDate = fromDate.Value.AddDays(-7); + } else if (TimeSpanInput.SelectedIndex == 1) { + fromDate = fromDate.Value.AddMonths(-6); + } else { + fromDate = null; + } + var mails = await Utils.GetSentMails(fromDate: fromDate == null ? null : DateOnly.FromDateTime(fromDate.Value)); + Data = mails.Reverse().Select(m => new Row( + m.DateTime, + m.Type == "email" ? "E-Mail" : m.Type == "postal" ? "Post" : "?", + m.MgNr, + m.Name, + string.Join("\n", m.Addresses), + m.Subject, + string.Join("\n", m.Attachments) + )).ToList(); + MailList.ItemsSource = Data; + ApplyFilters(); + } + + private void FilterInput_TextChanged(object sender, RoutedEventArgs evt) { + ApplyFilters(); + } + + private void TypeInput_SelectionChanged(object sender, RoutedEventArgs evt) { + ApplyFilters(); + } + + private void ApplyFilters() { + var filters = FilterInput.Text.Split(' '); + IEnumerable data = Data; + switch (TypeInput.SelectedIndex) { + case 1: data = data.Where(d => d.Type == "Post"); break; + case 2: data = data.Where(d => d.Type == "E-Mail"); break; + } + foreach (var filter in filters) { + if (int.TryParse(filter, out var mgnr)) { + data = data.Where(d => d.MgNr == mgnr); + } else { + var f = filter.ToLower(); + data = data.Where(d => d.Name.Contains(f, StringComparison.CurrentCultureIgnoreCase) || + d.Addresses.Contains(f, StringComparison.CurrentCultureIgnoreCase) || + d.Subject.Contains(f, StringComparison.CurrentCultureIgnoreCase) || + d.Attachments.Contains(f, StringComparison.CurrentCultureIgnoreCase)); + } + } + if (IsLoaded) + MailList.ItemsSource = data.ToList(); + } + + private async void MailList_SelectionChanged(object sender, RoutedEventArgs evt) { + if (MailList.SelectedItem is not Row item) return; + if (item.Type == "E-Mail") { + MailData.Text = await Utils.FindSentMailBody(item.DateTime); + } else { + MailData.Text = ""; + } + } + } +} diff --git a/Elwig/Windows/MailWindow.xaml b/Elwig/Windows/MailWindow.xaml index e7b8405..8ed4002 100644 --- a/Elwig/Windows/MailWindow.xaml +++ b/Elwig/Windows/MailWindow.xaml @@ -27,281 +27,334 @@ - - - - - - - + + + + + + - + + + + + + + + + + + + + - - + - - - - -