diff --git a/Elwig/Helpers/Billing/BillingData.cs b/Elwig/Helpers/Billing/BillingData.cs index 4bd0f6b..45396e7 100644 --- a/Elwig/Helpers/Billing/BillingData.cs +++ b/Elwig/Helpers/Billing/BillingData.cs @@ -150,38 +150,33 @@ namespace Elwig.Helpers.Billing { return dict; } - protected static Dictionary GetSelection(JsonNode value, IEnumerable vaributes) { + protected static Dictionary GetSelection(JsonNode value, IEnumerable vaributes) { if (value is JsonValue flatRate) { return vaributes.ToDictionary(e => e, _ => flatRate); } if (value is not JsonObject data) { throw new InvalidOperationException(); } - Dictionary dict; + Dictionary dict; if (data["default"] is JsonValue def) { dict = vaributes.ToDictionary(e => e, _ => def); } else { dict = []; } - var varieties = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length == 2); - var attributes = data.Where(p => p.Key.StartsWith('/')); - var others = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length > 2 && p.Key != "default"); - foreach (var (idx, v) in varieties) { + var conv = data + .Where(p => p.Key != "default") + .Select(p => (new RawVaribute(p.Key), p.Value)) + .OrderBy(p => (p.Item1.SortId != null ? 10 : 0) + (p.Item1.AttrId != null ? 12 : 0) + (p.Item1.CultId != null ? 11 : 0)) + .ToList(); + foreach (var (idx, v) in conv) { var curve = v?.AsValue() ?? throw new InvalidOperationException(); - foreach (var i in vaributes.Where(e => e.StartsWith(idx[..^1]))) { + foreach (var i in vaributes.Where(e => + (idx.SortId == null || idx.SortId == e.SortId) && + (idx.AttrId == null || idx.AttrId == e.AttrId) && + (idx.CultId == null || idx.CultId == e.CultId))) { dict[i] = curve; } } - foreach (var (idx, v) in attributes) { - var curve = v?.AsValue() ?? throw new InvalidOperationException(); - foreach (var i in vaributes.Where(e => e[2..] == idx[1..])) { - dict[i] = curve; - } - } - foreach (var (idx, v) in others) { - var curve = v?.AsValue() ?? throw new InvalidOperationException(); - dict[idx.Replace("/", "")] = curve; - } return dict; } @@ -257,7 +252,7 @@ namespace Elwig.Helpers.Billing { return curve; } - protected static void CollapsePaymentData(JsonObject data, IEnumerable vaributes, bool useDefault = true) { + protected static void CollapsePaymentData(JsonObject data, IEnumerable vaributes, bool useDefault = true) { Dictionary> rev1 = []; Dictionary> rev2 = []; foreach (var (k, v) in data) { @@ -292,23 +287,23 @@ namespace Elwig.Helpers.Billing { var attributes = data .Select(e => e.Key) .Where(k => k.Length > 3 && k.Contains('/')) - .Select(k => "/" + k.Split('/')[1]) + .Select(k => k.Split('/')[1]) .Distinct() .ToList(); foreach (var idx in attributes) { - var len = vaributes.Count(e => e.EndsWith(idx)); + var len = vaributes.Count(e => e.AttrId == idx); foreach (var (v, ks) in rev1) { var myKs = ks.Where(k => k.EndsWith(idx)).ToList(); if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) { foreach (var k in myKs) data.Remove(k); - data[idx] = v; + data[$"/{idx}"] = v; } } foreach (var (v, ks) in rev2) { var myKs = ks.Where(k => k.EndsWith(idx)).ToList(); if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) { foreach (var k in myKs) data.Remove(k); - data[idx] = v; + data[$"/{idx}"] = v; } } } @@ -317,7 +312,7 @@ namespace Elwig.Helpers.Billing { public static JsonObject FromGraphEntries( IEnumerable graphEntries, BillingData? origData = null, - IEnumerable? vaributes = null, + IEnumerable? vaributes = null, bool useDefaultPayment = true, bool useDefaultQuality = true ) { @@ -338,16 +333,16 @@ namespace Elwig.Helpers.Billing { continue; } foreach (var c in entry.Vaributes) { - if (entry.Abgewertet) { - qualityWei[$"{c.Variety?.SortId}/{c.Attribute?.AttrId}"] = node.DeepClone(); + if (entry.Abgewertet) {; + qualityWei[c.ToString()] = node.DeepClone(); } else { - payment[$"{c.Variety?.SortId}/{c.Attribute?.AttrId}"] = node.DeepClone(); + payment[c.ToString()] = node.DeepClone(); } } } - CollapsePaymentData(payment, vaributes ?? payment.Select(e => e.Key).ToList(), useDefaultPayment); - CollapsePaymentData(qualityWei, vaributes ?? qualityWei.Select(e => e.Key).ToList(), useDefaultQuality); + CollapsePaymentData(payment, vaributes ?? payment.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultPayment); + CollapsePaymentData(qualityWei, vaributes ?? qualityWei.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultQuality); var data = new JsonObject { ["mode"] = "elwig", diff --git a/Elwig/Helpers/Billing/EditBillingData.cs b/Elwig/Helpers/Billing/EditBillingData.cs index 415f123..f721723 100644 --- a/Elwig/Helpers/Billing/EditBillingData.cs +++ b/Elwig/Helpers/Billing/EditBillingData.cs @@ -7,18 +7,18 @@ using System.Text.Json.Nodes; namespace Elwig.Helpers.Billing { public class EditBillingData : BillingData { - protected readonly IEnumerable Vaributes; + protected readonly IEnumerable Vaributes; - public EditBillingData(JsonObject data, IEnumerable vaributes) : + public EditBillingData(JsonObject data, IEnumerable vaributes) : base(data) { Vaributes = vaributes; } - public static EditBillingData FromJson(string json, IEnumerable vaributes) { + public static EditBillingData FromJson(string json, IEnumerable vaributes) { return new(ParseJson(json), vaributes); } - private (Dictionary, Dictionary>) GetGraphEntries(JsonNode root) { + private (Dictionary, Dictionary>) GetGraphEntries(JsonNode root) { Dictionary> dict1 = []; Dictionary> dict2 = []; if (root is JsonObject paymentObj) { @@ -55,7 +55,7 @@ namespace Elwig.Helpers.Billing { curves[i + virtOffset] = new Curve(CurveMode.Oe, new() { { 73, idx } }, null); } - Dictionary> dict3 = curves.ToDictionary(c => c.Key, _ => new List()); + Dictionary> dict3 = curves.ToDictionary(c => c.Key, _ => new List()); foreach (var (selector, value) in GetSelection(root, Vaributes)) { int? idx = null; if (value.TryGetValue(out var val)) { @@ -73,13 +73,14 @@ namespace Elwig.Helpers.Billing { private static List CreateGraphEntries( AppDbContext ctx, int precision, Dictionary curves, - Dictionary> entries + Dictionary> entries ) { var vars = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v); var attrs = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a); + var cults = ctx.WineCultivations.ToDictionary(c => c.CultId, c => c); return entries .Select(e => new GraphEntry(e.Key, precision, curves[e.Key], e.Value - .Select(s => new Varibute(vars[s[..2]], s.Length > 2 ? attrs[s[2..]] : null)) + .Select(s => new Varibute(s, vars, attrs, cults)) .ToList())) .ToList(); } diff --git a/Elwig/Helpers/Billing/PaymentBillingData.cs b/Elwig/Helpers/Billing/PaymentBillingData.cs index ba33ed1..e5a75ff 100644 --- a/Elwig/Helpers/Billing/PaymentBillingData.cs +++ b/Elwig/Helpers/Billing/PaymentBillingData.cs @@ -7,40 +7,38 @@ namespace Elwig.Helpers.Billing { public class PaymentBillingData : BillingData { protected readonly Dictionary Curves; - protected readonly Dictionary PaymentData; - protected readonly Dictionary QualityData; - protected readonly IEnumerable Vaributes; + protected readonly Dictionary PaymentData; + protected readonly Dictionary QualityData; + protected readonly IEnumerable Vaributes; - public PaymentBillingData(JsonObject data, IEnumerable vaributes) : + public PaymentBillingData(JsonObject data, IEnumerable vaributes) : base(data) { - if (vaributes.Any(e => e.Any(c => c < 'A' || c > 'Z'))) - throw new ArgumentException("Invalid vaributes"); Vaributes = vaributes; Curves = GetCurves(); PaymentData = GetPaymentData(); QualityData = GetQualityData(); } - public static PaymentBillingData FromJson(string json, IEnumerable vaributes) { + public static PaymentBillingData FromJson(string json, IEnumerable vaributes) { return new(ParseJson(json), vaributes); } - private Dictionary GetData(JsonNode data) { + private Dictionary GetData(JsonNode data) { return GetSelection(data, Vaributes).ToDictionary(e => e.Key, e => LookupCurve(e.Value)); } - protected Dictionary GetPaymentData() { + protected Dictionary GetPaymentData() { return GetData(GetPaymentEntry()); } - protected Dictionary GetQualityData() { - Dictionary dict = []; + protected Dictionary GetQualityData() { + Dictionary dict = []; var q = GetQualityEntry(); if (q == null) return dict; foreach (var (qualid, data) in q) { foreach (var (idx, d) in GetData(data ?? throw new InvalidOperationException())) { - dict[$"{qualid}/{idx}"] = d; + dict[new(qualid, idx.SortId, idx.AttrId, idx.CultId)] = d; } } @@ -63,11 +61,11 @@ namespace Elwig.Helpers.Billing { } protected Curve GetCurve(string sortid, string? attrid) { - return PaymentData[$"{sortid}{attrid}"]; + return PaymentData[new(sortid, attrid ?? "", null)]; } protected Curve? GetQualityCurve(string qualid, string sortid, string? attrid) { - return QualityData.TryGetValue($"{qualid}/{sortid}{attrid}", out var curve) ? curve : null; + return QualityData.TryGetValue(new(qualid, sortid, attrid ?? "", null), out var curve) ? curve : null; } } } diff --git a/Elwig/Helpers/Billing/Varibute.cs b/Elwig/Helpers/Billing/Varibute.cs index 7cfe8c0..60cedb5 100644 --- a/Elwig/Helpers/Billing/Varibute.cs +++ b/Elwig/Helpers/Billing/Varibute.cs @@ -1,20 +1,81 @@ using Elwig.Models.Entities; using System; +using System.Collections.Generic; namespace Elwig.Helpers.Billing { + + public record struct RawQualVaribute { + public string QualId; + public string? SortId; + public string? AttrId; + public string? CultId; + + public RawQualVaribute(string qualid, string? sortid, string? attrid, string? cultid) { + QualId = qualid; + SortId = sortid; + AttrId = attrid; + CultId = cultid; + } + } + + public record struct RawVaribute : IComparable { + public string? SortId; + public string? AttrId; + public string? CultId; + + public RawVaribute(string? sortid, string? attrid, string? cultid) { + SortId = sortid; + AttrId = attrid; + CultId = cultid; + } + + public RawVaribute(string id) { + var p1 = id.Split('/')[0].Split('-')[0]; + SortId = p1 == "" ? null : p1; + AttrId = id.Contains('/') ? id.Split('/')[1].Split('-')[0] : null; + CultId = id.Contains('-') ? id.Split('-')[1] : null; + } + + public readonly override string ToString() { + return $"{SortId}" + (AttrId != null ? $"/{AttrId}" : "") + (CultId != null ? $"-{CultId}" : ""); + } + + public readonly int CompareTo(RawVaribute other) { + return $"{SortId}/{AttrId}-{CultId}".CompareTo($"{other.SortId}/{other.AttrId}-{other.CultId}"); + } + } + public class Varibute : IComparable { public WineVar? Variety { get; } public WineAttr? Attribute { get; } + public WineCult? Cultivation { get; } public int? AssignedGraphId { get; set; } public int? AssignedAbgewGraphId { get; set; } - public string Listing => $"{Variety?.SortId}{Attribute?.AttrId}"; - public string FullName => $"{Variety?.Name}" + (Variety != null && Attribute != null ? " " : "") + $"{Attribute?.Name}"; + public string Listing => $"{Variety?.SortId}" + + (Attribute != null ? $"/{Attribute.AttrId}" : "") + + (Cultivation != null ? $"-{Cultivation.CultId}" : ""); + public string FullName => $"{Variety?.Name}" + + (Variety != null && Attribute != null ? " " : "") + $"{Attribute?.Name}" + + ((Variety != null || Attribute != null) && Cultivation != null ? " " : "") + $"{Cultivation?.Name}"; - public Varibute(WineVar? var, WineAttr? attr) { + public Varibute(RawVaribute raw) : + this(raw.SortId != null ? new WineVar(raw.SortId, raw.SortId) : null, + raw.AttrId != null ? new WineAttr(raw.AttrId, raw.AttrId) : null, + raw.CultId != null ? new WineCult(raw.CultId, raw.CultId) : null) { + } + + public Varibute(RawVaribute raw, Dictionary vars, Dictionary attrs, Dictionary cults) : + this(raw.SortId != null && raw.SortId != "" ? vars[raw.SortId] : null, + raw.AttrId != null && raw.AttrId != "" ? attrs[raw.AttrId] : null, + raw.CultId != null && raw.CultId != "" ? cults[raw.CultId] : null) { + } + + public Varibute(WineVar? var, WineAttr? attr, WineCult? cult) { Variety = var; Attribute = attr; + Cultivation = cult; } public override string ToString() { @@ -23,6 +84,6 @@ namespace Elwig.Helpers.Billing { public int CompareTo(Varibute? other) { return Listing.CompareTo(other?.Listing); - } + } } } diff --git a/Elwig/Helpers/Utils.cs b/Elwig/Helpers/Utils.cs index cb625a8..564eb1d 100644 --- a/Elwig/Helpers/Utils.cs +++ b/Elwig/Helpers/Utils.cs @@ -362,11 +362,11 @@ namespace Elwig.Helpers { return output.OrderByDescending(l => l.Count()); } - public static List GetVaributes(AppDbContext ctx, int year, bool withSlash = false, bool onlyDelivered = true) { - var varieties = ctx.WineVarieties.Select(v => v.SortId).ToList(); + public static List GetVaributes(AppDbContext ctx, int year, bool onlyDelivered = true) { + var varieties = ctx.WineVarieties.Select(v => new RawVaribute(v.SortId, "", null)).ToList(); var delivered = ctx.DeliveryParts .Where(d => d.Year == year) - .Select(d => $"{d.SortId}{(withSlash ? "/" : "")}{d.AttrId}") + .Select(d => new RawVaribute(d.SortId, d.AttrId ?? "", d.CultId)) .Distinct() .ToList(); return [.. (onlyDelivered ? delivered : delivered.Union(varieties)).Order()]; @@ -375,8 +375,9 @@ namespace Elwig.Helpers { public static List GetVaributeList(AppDbContext ctx, int year, bool onlyDelivered = true) { var varieties = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v); var attributes = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a); - return GetVaributes(ctx, year, false, onlyDelivered) - .Select(s => new Varibute(varieties[s[..2]], s.Length > 2 ? attributes[s[2..]] : null)) + var cultivations = ctx.WineCultivations.ToDictionary(c => c.CultId, c => c); + return GetVaributes(ctx, year, onlyDelivered) + .Select(s => new Varibute(s, varieties, attributes, cultivations)) .ToList(); } } diff --git a/Elwig/Models/Entities/WineCult.cs b/Elwig/Models/Entities/WineCult.cs index 064ef03..fed7738 100644 --- a/Elwig/Models/Entities/WineCult.cs +++ b/Elwig/Models/Entities/WineCult.cs @@ -13,5 +13,16 @@ namespace Elwig.Models.Entities { [Column("description")] public string? Description { get; set; } + + public WineCult() { } + + public WineCult(string cultId, string name) { + CultId = cultId; + Name = name; + } + + public override string ToString() { + return Name; + } } } diff --git a/Elwig/Windows/ChartWindow.xaml b/Elwig/Windows/ChartWindow.xaml index cd51090..80cf2d3 100644 --- a/Elwig/Windows/ChartWindow.xaml +++ b/Elwig/Windows/ChartWindow.xaml @@ -69,7 +69,8 @@ - + + @@ -78,7 +79,7 @@ + VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,0,0" Grid.Column="1"/> diff --git a/Elwig/Windows/ChartWindow.xaml.cs b/Elwig/Windows/ChartWindow.xaml.cs index cc1a86a..40bf85e 100644 --- a/Elwig/Windows/ChartWindow.xaml.cs +++ b/Elwig/Windows/ChartWindow.xaml.cs @@ -644,7 +644,7 @@ namespace Elwig.Windows { private async void SaveButton_Click(object sender, RoutedEventArgs e) { var origData = BillingData.FromJson(PaymentVar.Data); - var data = BillingData.FromGraphEntries(GraphEntries, origData, Utils.GetVaributes(Context, Year, withSlash: true), + var data = BillingData.FromGraphEntries(GraphEntries, origData, Utils.GetVaributes(Context, Year), AllVaributesAssigned, AllVaributesAssignedAbgew); EntityEntry? tr = null; diff --git a/Tests/HelperTests/BillingDataTest.cs b/Tests/HelperTests/BillingDataTest.cs index 617b801..4689ec0 100644 --- a/Tests/HelperTests/BillingDataTest.cs +++ b/Tests/HelperTests/BillingDataTest.cs @@ -8,7 +8,9 @@ namespace Tests.HelperTests { public class BillingDataTest { private static readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true }; - private static readonly string[] Vaributes = ["GV", "GVD", "GVK", "GVS", "GVZ", "WR", "WRS", "ZW", "ZWS", "ZWZ"]; + private static readonly RawVaribute[] Vaributes = [ + new("GV/"), new("GV/D"), new("GV/K"), new("GV/S"), new("GV/Z"), + new("WR/"), new("WR/S"), new("ZW/"), new("ZW/S"), new("ZW/Z")]; private static (string, string?) GetSortIdAttrId(string bucket) { return (bucket[..2], bucket.Length > 2 ? bucket[2..] : null); @@ -345,14 +347,7 @@ namespace Tests.HelperTests { } private static List GetSelection(IEnumerable attVars) { - return attVars.Select(s => { - var sortid = s[..2]; - var attrid = s.Length > 2 ? s[2..] : null; - return new Varibute( - new WineVar(sortid, sortid), - attrid == null ? null : new WineAttr(attrid, attrid) - ); - }).ToList(); + return attVars.Select(s => new Varibute(new RawVaribute(s))).ToList(); } [Test] @@ -378,7 +373,7 @@ namespace Tests.HelperTests { List entries = [ new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { [73] = 0.5m - }, null), GetSelection(["GV"])) + }, null), GetSelection(["GV/"])) ]; var data = BillingData.FromGraphEntries(entries); Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo(""" @@ -397,7 +392,7 @@ namespace Tests.HelperTests { new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { [73] = 0.5m, [83] = 1.0m - }, null), GetSelection(["GV"])) + }, null), GetSelection(["GV/"])) ]; var data = BillingData.FromGraphEntries(entries); Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo(""" @@ -425,10 +420,10 @@ namespace Tests.HelperTests { new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { [73] = 0.5m, [84] = 1.0m - }, null), GetSelection(["GV", "ZW"])), + }, null), GetSelection(["GV/", "ZW/"])), new GraphEntry(10, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { [73] = 0.75m, - }, null), GetSelection(["WR"])) + }, null), GetSelection(["WR/"])) ]; var data = BillingData.FromGraphEntries(entries); Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo(""" @@ -459,14 +454,14 @@ namespace Tests.HelperTests { new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { [73] = 0.5m, [84] = 1.0m - }, null), GetSelection(["GVB", "ZWB"])), + }, null), GetSelection(["GV/B", "ZW/B"])), new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { [73] = 0.75m, - }, null), GetSelection(["WR", "BL", "RR", "FV"])), + }, null), GetSelection(["WR/", "BL/", "RR/", "FV/"])), new GraphEntry(4, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { [73] = 0.65m, [84] = 1.2m - }, null), GetSelection(["BP", "SA"])) + }, null), GetSelection(["BP/", "SA/"])) ]; var data = BillingData.FromGraphEntries(entries); Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""