From 60359935a4440e6caaa3cdf79e231cd899b2f8dc Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Thu, 4 Jul 2024 19:12:15 +0200 Subject: [PATCH] [#10] DeliveryAdminWindow: Move generation of tooltips to DeliveryService --- Elwig/Services/DeliveryService.cs | 149 ++++++++++++++++++++ Elwig/ViewModels/DeliveryAdminViewModel.cs | 16 ++- Elwig/Windows/DeliveryAdminWindow.xaml | 40 ++---- Elwig/Windows/DeliveryAdminWindow.xaml.cs | 150 ++------------------- 4 files changed, 182 insertions(+), 173 deletions(-) diff --git a/Elwig/Services/DeliveryService.cs b/Elwig/Services/DeliveryService.cs index 8e0194d..ff3e2f8 100644 --- a/Elwig/Services/DeliveryService.cs +++ b/Elwig/Services/DeliveryService.cs @@ -17,6 +17,7 @@ using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; using System.IO; using Microsoft.EntityFrameworkCore.ChangeTracking; +using System.Windows.Controls; namespace Elwig.Services { public static class DeliveryService { @@ -636,5 +637,153 @@ namespace Elwig.Services { } Mouse.OverrideCursor = null; } + + private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) { + var tb = new TextBlock() { + Text = text, + TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left, + Margin = new(0, 12 * row, 0, 0), + FontWeight = bold ? FontWeights.Bold : FontWeights.Normal, + }; + tb.SetValue(Grid.ColumnProperty, col); + tb.SetValue(Grid.ColumnSpanProperty, colSpan); + grid.Children.Add(tb); + } + + private static void AddWeightToolTipRow(Grid grid, int row, string? h1, string? h2, int weight, int? total1, int total2) { + var bold = h2 == null; + if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold); + if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold); + AddToolTipCell(grid, $"{weight:N0} kg", row, 2, 1, bold, true); + if (total1 != null && total1 != 0) + AddToolTipCell(grid, $"{weight * 100.0 / total1:N1} %", row, 3, 1, bold, true); + if (total2 != 0) + AddToolTipCell(grid, $"{weight * 100.0 / total2:N1} %", row, 4, 1, bold, true); + } + + private static void AddGradationToolTipRow(Grid grid, int row, string? h1, string? h2, double min, double avg, double max) { + var bold = h2 == null; + if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold); + if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold); + AddToolTipCell(grid, $"{min:N1}°", row, 2, 1, bold, true); + AddToolTipCell(grid, $"{avg:N1}°", row, 3, 1, bold, true); + AddToolTipCell(grid, $"{max:N1}°", row, 4, 1, bold, true); + } + + public static async Task<(string WeightText, Grid WeightGrid, string GradationText, Grid GradationGrid)> GenerateToolTip(IQueryable deliveryParts) { + var wGrid = new Grid(); + wGrid.ColumnDefinitions.Add(new() { Width = new(10) }); + wGrid.ColumnDefinitions.Add(new() { Width = new(60) }); + wGrid.ColumnDefinitions.Add(new() { Width = new(80) }); + wGrid.ColumnDefinitions.Add(new() { Width = new(50) }); + wGrid.ColumnDefinitions.Add(new() { Width = new(50) }); + var wText = "-"; + + var gGrid = new Grid(); + gGrid.ColumnDefinitions.Add(new() { Width = new(10) }); + gGrid.ColumnDefinitions.Add(new() { Width = new(60) }); + gGrid.ColumnDefinitions.Add(new() { Width = new(35) }); + gGrid.ColumnDefinitions.Add(new() { Width = new(35) }); + gGrid.ColumnDefinitions.Add(new() { Width = new(35) }); + AddToolTipCell(gGrid, "Min.", 0, 2, 1, false, false, true); + AddToolTipCell(gGrid, "⌀", 0, 3, 1, false, false, true); + AddToolTipCell(gGrid, "Max.", 0, 4, 1, false, false, true); + var gText = "-"; + + var weight = await deliveryParts.SumAsync(p => p.Weight); + wText = $"{weight:N0} kg"; + AddWeightToolTipRow(wGrid, 0, "Gewicht", null, weight, null, weight); + + if (await deliveryParts.AnyAsync()) { + var kmwMin = await deliveryParts.MinAsync(p => p.Kmw); + var kmwAvg = Utils.AggregateDeliveryPartsKmw(deliveryParts); + var kmwMax = await deliveryParts.MaxAsync(p => p.Kmw); + gText = $"{kmwMin:N1}° / {kmwAvg:N1}° / {kmwMax:N1}°"; + AddGradationToolTipRow(gGrid, 1, "Gradation", null, kmwMin, kmwAvg, kmwMax); + + var attrGroups = await deliveryParts + .GroupBy(p => new { Attr = p.Attribute!.Name, Cult = p.Cultivation!.Name }) + .Select(g => new { + g.Key.Attr, + g.Key.Cult, + Weight = g.Sum(p => p.Weight), + Min = g.Min(p => p.Kmw), + Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight), + Max = g.Max(p => p.Kmw), + }) + .OrderByDescending(g => g.Weight) + .ThenBy(g => g.Attr) + .ToListAsync(); + var sortGroups = await deliveryParts + .GroupBy(p => p.SortId) + .Select(g => new { + SortId = g.Key, + Weight = g.Sum(p => p.Weight), + Min = g.Min(p => p.Kmw), + Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight), + Max = g.Max(p => p.Kmw), + }) + .OrderByDescending(g => g.Weight) + .ThenBy(g => g.SortId) + .ToListAsync(); + var groups = await deliveryParts + .GroupBy(p => new { + Attr = p.Attribute!.Name, + Cult = p.Cultivation!.Name, + p.SortId, + }) + .Select(g => new { + g.Key.Attr, + g.Key.Cult, + g.Key.SortId, + Weight = g.Sum(p => p.Weight), + Min = g.Min(p => p.Kmw), + Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight), + Max = g.Max(p => p.Kmw) + }) + .OrderByDescending(g => g.SortId) + .ThenBy(g => g.Attr) + .ThenBy(g => g.SortId) + .ToListAsync(); + + int rowNum = 1; + foreach (var attrG in attrGroups) { + rowNum++; + var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult; + AddWeightToolTipRow(wGrid, rowNum++, name, null, attrG.Weight, attrG.Weight, weight); + foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) { + AddWeightToolTipRow(wGrid, rowNum++, null, g.SortId, g.Weight, attrG.Weight, weight); + } + } + rowNum = 2; + foreach (var attrG in attrGroups) { + rowNum++; + var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult; + AddGradationToolTipRow(gGrid, rowNum++, name, null, attrG.Min, attrG.Avg, attrG.Max); + foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) { + AddGradationToolTipRow(gGrid, rowNum++, null, g.SortId, g.Min, g.Avg, g.Max); + } + } + + if (attrGroups.Count == 1) { + var g = attrGroups.First(); + var name = g.Attr == null && g.Cult == null ? null : g.Attr + (g.Attr != null && g.Cult != null ? " / " : "") + g.Cult; + if (name != null) { + wText += $" [{name}]"; + gText += $" [{name}]"; + } + if (sortGroups.Count > 1 && sortGroups.Count <= 4) { + wText += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.SortId == null ? "" : $" [{g.SortId}]")))}"; + gText += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (g.SortId == null ? "" : $" [{g.SortId}]")))}"; + + } + } else if (attrGroups.Count <= 4) { + wText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}"; + gText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}"; + } + } + + return (wText, wGrid, gText, gGrid); + } } } diff --git a/Elwig/ViewModels/DeliveryAdminViewModel.cs b/Elwig/ViewModels/DeliveryAdminViewModel.cs index 0b3d90a..89be54a 100644 --- a/Elwig/ViewModels/DeliveryAdminViewModel.cs +++ b/Elwig/ViewModels/DeliveryAdminViewModel.cs @@ -4,6 +4,7 @@ using Elwig.Models.Entities; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Windows.Controls; namespace Elwig.ViewModels { public partial class DeliveryAdminViewModel : ObservableObject { @@ -158,14 +159,19 @@ namespace Elwig.ViewModels { private bool? _isHandPicked; [ObservableProperty] - private string _statusMembers = "Mitglieder: -"; + private string _statusMembers = "-"; [ObservableProperty] - private string _statusDeliveries = "Lieferungen: -"; + private string _statusDeliveries = "-"; [ObservableProperty] - private string _statusVarieties = "Sorten: -"; + private string _statusVarieties = "-"; [ObservableProperty] - private string _statusWeight = "Gewicht: -"; + private string _statusWeight = "-"; [ObservableProperty] - private string _statusGradation = "Gradation: -"; + private string _statusGradation = "-"; + + [ObservableProperty] + private Grid? _statusWeightToolTip; + [ObservableProperty] + private Grid? _statusGradationToolTip; } } diff --git a/Elwig/Windows/DeliveryAdminWindow.xaml b/Elwig/Windows/DeliveryAdminWindow.xaml index 4513ca3..3050e10 100644 --- a/Elwig/Windows/DeliveryAdminWindow.xaml +++ b/Elwig/Windows/DeliveryAdminWindow.xaml @@ -618,46 +618,32 @@ - + + Mitglieder: + - + + Lieferungen: + - + + Sorten: + - - - - - - - - - - - - + + Gewicht: - - - - - - - - - - - - + + Gradation: diff --git a/Elwig/Windows/DeliveryAdminWindow.xaml.cs b/Elwig/Windows/DeliveryAdminWindow.xaml.cs index 3e5ef86..a7574f4 100644 --- a/Elwig/Windows/DeliveryAdminWindow.xaml.cs +++ b/Elwig/Windows/DeliveryAdminWindow.xaml.cs @@ -334,38 +334,6 @@ namespace Elwig.Windows { } } - private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) { - var tb = new TextBlock() { - Text = text, - TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left, - Margin = new(0, 12 * row, 0, 0), - FontWeight = bold ? FontWeights.Bold : FontWeights.Normal, - }; - tb.SetValue(Grid.ColumnProperty, col); - tb.SetValue(Grid.ColumnSpanProperty, colSpan); - grid.Children.Add(tb); - } - - private void AddWeightToolTipRow(int row, string? h1, string? h2, int weight, int? total1, int total2) { - var bold = h2 == null; - if (h1 != null) AddToolTipCell(StatusWeightToolTip, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold); - if (h2 != null) AddToolTipCell(StatusWeightToolTip, h2 + ":", row, 1, 1, bold); - AddToolTipCell(StatusWeightToolTip, $"{weight:N0} kg", row, 2, 1, bold, true); - if (total1 != null && total1 != 0) - AddToolTipCell(StatusWeightToolTip, $"{weight * 100.0 / total1:N1} %", row, 3, 1, bold, true); - if (total2 != 0) - AddToolTipCell(StatusWeightToolTip, $"{weight * 100.0 / total2:N1} %", row, 4, 1, bold, true); - } - - private void AddGradationToolTipRow(int row, string? h1, string? h2, double min, double avg, double max) { - var bold = h2 == null; - if (h1 != null) AddToolTipCell(StatusGradationToolTip, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold); - if (h2 != null) AddToolTipCell(StatusGradationToolTip, h2 + ":", row, 1, 1, bold); - AddToolTipCell(StatusGradationToolTip, $"{min:N1}°", row, 2, 1, bold, true); - AddToolTipCell(StatusGradationToolTip, $"{avg:N1}°", row, 3, 1, bold, true); - AddToolTipCell(StatusGradationToolTip, $"{max:N1}°", row, 4, 1, bold, true); - } - private async Task RefreshList(bool updateSort = false) { using var ctx = new AppDbContext(); var (_, deliveryQuery, deliveryPartsQuery, predicate, filter) = await ViewModel.GetFilters(ctx); @@ -397,120 +365,20 @@ namespace Elwig.Windows { await RefreshDeliveryParts(); var members = deliveries.Select(d => d.Member).DistinctBy(m => m.MgNr).ToList(); - ViewModel.StatusMembers = $"Mitglieder: {members.Count}" + (members.Count > 0 && members.Count <= 4 ? $" ({string.Join(", ", members.Select(m => m.AdministrativeName))})" : ""); - ViewModel.StatusDeliveries = $"Lieferungen: {deliveries.Count}"; + ViewModel.StatusMembers = $"{members.Count}" + (members.Count > 0 && members.Count <= 4 ? $" ({string.Join(", ", members.Select(m => m.AdministrativeName))})" : ""); + ViewModel.StatusDeliveries = $"{deliveries.Count}"; if (filter.Count == 0) { var deliveryParts = deliveryPartsQuery; - var n = await deliveryParts.CountAsync(); - ViewModel.StatusDeliveries = $"Lieferungen: {deliveries.Count} ({n})"; + ViewModel.StatusDeliveries = $"{deliveries.Count} ({await deliveryParts.CountAsync()})"; var varieties = await deliveryParts.Select(d => d.SortId).Distinct().ToListAsync(); - ViewModel.StatusVarieties = $"Sorten: {varieties.Count}" + (varieties.Count > 0 && varieties.Count <= 10 ? $" ({string.Join(", ", varieties)})" : ""); - - StatusWeightToolTip.Children.Clear(); - StatusGradationToolTip.Children.Clear(); - - var weight = await deliveryParts.SumAsync(p => p.Weight); - ViewModel.StatusWeight = $"Gewicht: {weight:N0} kg"; - AddWeightToolTipRow(0, "Gewicht", null, weight, null, weight); - - if (n > 0) { - var kmwMin = await deliveryParts.MinAsync(p => p.Kmw); - var kmwAvg = Utils.AggregateDeliveryPartsKmw(deliveryParts); - var kmwMax = await deliveryParts.MaxAsync(p => p.Kmw); - ViewModel.StatusGradation = $"Gradation: {kmwMin:N1}° / {kmwAvg:N1}° / {kmwMax:N1}°"; - AddToolTipCell(StatusGradationToolTip, "Min.", 0, 2, 1, false, false, true); - AddToolTipCell(StatusGradationToolTip, "⌀", 0, 3, 1, false, false, true); - AddToolTipCell(StatusGradationToolTip, "Max.", 0, 4, 1, false, false, true); - AddGradationToolTipRow(1, "Gradation", null, kmwMin, kmwAvg, kmwMax); - - var attrGroups = await deliveryParts - .GroupBy(p => new { Attr = p.Attribute!.Name, Cult = p.Cultivation!.Name }) - .Select(g => new { - g.Key.Attr, - g.Key.Cult, - Weight = g.Sum(p => p.Weight), - Min = g.Min(p => p.Kmw), - Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight), - Max = g.Max(p => p.Kmw), - }) - .OrderByDescending(g => g.Weight) - .ThenBy(g => g.Attr) - .ToListAsync(); - var sortGroups = await deliveryParts - .GroupBy(p => p.SortId) - .Select(g => new { - SortId = g.Key, - Weight = g.Sum(p => p.Weight), - Min = g.Min(p => p.Kmw), - Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight), - Max = g.Max(p => p.Kmw), - }) - .OrderByDescending(g => g.Weight) - .ThenBy(g => g.SortId) - .ToListAsync(); - var groups = await deliveryParts - .GroupBy(p => new { - Attr = p.Attribute!.Name, - Cult = p.Cultivation!.Name, - p.SortId, - }) - .Select(g => new { - g.Key.Attr, - g.Key.Cult, - g.Key.SortId, - Weight = g.Sum(p => p.Weight), - Min = g.Min(p => p.Kmw), - Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight), - Max = g.Max(p => p.Kmw) - }) - .OrderByDescending(g => g.SortId) - .ThenBy(g => g.Attr) - .ThenBy(g => g.SortId) - .ToListAsync(); - - int rowNum = 1; - foreach (var attrG in attrGroups) { - rowNum++; - var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult; - AddWeightToolTipRow(rowNum++, name, null, attrG.Weight, attrG.Weight, weight); - foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) { - AddWeightToolTipRow(rowNum++, null, g.SortId, g.Weight, attrG.Weight, weight); - } - } - rowNum = 2; - foreach (var attrG in attrGroups) { - rowNum++; - var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult; - AddGradationToolTipRow(rowNum++, name, null, attrG.Min, attrG.Avg, attrG.Max); - foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) { - AddGradationToolTipRow(rowNum++, null, g.SortId, g.Min, g.Avg, g.Max); - } - } - - if (attrGroups.Count == 1) { - var g = attrGroups.First(); - var name = g.Attr == null && g.Cult == null ? null : g.Attr + (g.Attr != null && g.Cult != null ? " / " : "") + g.Cult; - if (name != null) { - ViewModel.StatusWeight += $" [{name}]"; - ViewModel.StatusGradation += $" [{name}]"; - } - if (sortGroups.Count > 1 && sortGroups.Count <= 4) { - ViewModel.StatusWeight += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.SortId == null ? "" : $" [{g.SortId}]")))}"; - ViewModel.StatusGradation += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (g.SortId == null ? "" : $" [{g.SortId}]")))}"; - - } - } else if (attrGroups.Count <= 4) { - ViewModel.StatusWeight += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}"; - ViewModel.StatusGradation += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}"; - } - } else { - ViewModel.StatusGradation = "Gradation: -"; - } + ViewModel.StatusVarieties = $"{varieties.Count}" + (varieties.Count > 0 && varieties.Count <= 10 ? $" ({string.Join(", ", varieties)})" : ""); + (ViewModel.StatusWeight, ViewModel.StatusWeightToolTip, + ViewModel.StatusGradation, ViewModel.StatusGradationToolTip) = await DeliveryService.GenerateToolTip(deliveryParts); } else { - ViewModel.StatusVarieties = "Sorten: -"; - ViewModel.StatusWeight = "Gewicht: -"; - ViewModel.StatusGradation = "Gradation: -"; + ViewModel.StatusVarieties = "-"; + ViewModel.StatusWeight = "-"; + ViewModel.StatusGradation = "-"; } if (updateSort && DeliveryList.SelectedItem != null)