Add the Broadcast Data dialog and send N1MM's score breakdown
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
using System.Globalization;
|
||||
using Nonemm.Core;
|
||||
|
||||
namespace Nonemm.Contests;
|
||||
|
||||
/// One row of N1MM's score query: the contacts of one band and mode, or of all
|
||||
/// of them when `Band` is `ScoreBreakdown.TotalBand`. `Kind` is empty except in
|
||||
/// WAE, which gives contacts and traffic a row each.
|
||||
public sealed record ScoreBreakdownRow(
|
||||
double Band,
|
||||
string Mode,
|
||||
string Kind,
|
||||
int Qsos,
|
||||
int Points,
|
||||
IReadOnlyList<int> Multipliers);
|
||||
|
||||
/// The score the way N1MM's database query splits it: a row per band and mode
|
||||
/// of the contact, with the multiplier flags added up, and a total row last
|
||||
/// when there is any contact.
|
||||
/// `ContestLog.Rows` splits by band alone, which is what the score window shows.
|
||||
public static class ScoreBreakdown
|
||||
{
|
||||
/// The band N1MM gives the total row.
|
||||
public const double TotalBand = 900_000_000;
|
||||
|
||||
public const string TotalMode = "Both";
|
||||
|
||||
/// Ordered the way the query orders them: by the band as text, so `10.0`
|
||||
/// comes before `3.5`, then by mode. Only contacts in the claimed score
|
||||
/// are counted.
|
||||
public static IReadOnlyList<ScoreBreakdownRow> For(IEnumerable<Qso> qsos, Contest contest)
|
||||
{
|
||||
List<Qso> counted = qsos.Where(q => q.IsInScore).ToList();
|
||||
List<ScoreBreakdownRow> rows = counted
|
||||
.Where(q => q.Mode.Name.Length > 0)
|
||||
.GroupBy(q => (Band: Rounded(q), Mode: q.Mode.Name, Kind: contest.RowKindFor(q)))
|
||||
.OrderBy(g => BandText(g.Key.Band), StringComparer.Ordinal)
|
||||
.ThenBy(g => g.Key.Mode, StringComparer.Ordinal)
|
||||
.ThenBy(g => g.Key.Kind, StringComparer.Ordinal)
|
||||
.Select(g => Row(g.Key.Band, g.Key.Mode, g.Key.Kind, g))
|
||||
.ToList();
|
||||
// the query's total is a group of the same rows, so an empty log has none
|
||||
if (counted.Count > 0)
|
||||
{
|
||||
rows.Add(Row(TotalBand, TotalMode, contest.HasQtcTraffic ? "All" : "", counted));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/// The band as the database stores it: the megahertz label to one decimal.
|
||||
private static double Rounded(Qso qso) =>
|
||||
Math.Round(qso.Band?.MegahertzLabel ?? 0, 1, MidpointRounding.AwayFromZero);
|
||||
|
||||
/// What SQLite writes for a real number cast to text.
|
||||
private static string BandText(double band) => band.ToString("0.0", CultureInfo.InvariantCulture);
|
||||
|
||||
private static ScoreBreakdownRow Row(double band, string mode, string kind, IEnumerable<Qso> qsos)
|
||||
{
|
||||
List<Qso> rows = [.. qsos];
|
||||
return new ScoreBreakdownRow(
|
||||
band,
|
||||
mode,
|
||||
kind,
|
||||
rows.Count,
|
||||
rows.Sum(q => q.Points),
|
||||
[
|
||||
rows.Count(q => q.IsMultiplier1),
|
||||
rows.Count(q => q.IsMultiplier2),
|
||||
rows.Count(q => q.IsMultiplier3),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user