@ham2k/extension-sdk 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activityScoring.js +40 -4
- package/dist/index.d.ts +6 -1
- package/docs/distribution.md +5 -2
- package/package.json +1 -1
package/dist/activityScoring.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createCachedTranslator } from "./i18n.js";
|
|
2
2
|
import { modesMatchForDupe } from "./modes.js";
|
|
3
|
+
import { parseCallsign } from "@ham2k/lib-callsigns";
|
|
3
4
|
import { fmtInteger } from "@ham2k/lib-format-tools";
|
|
4
5
|
import en from "./i18n/activityScoring.en.json";
|
|
5
6
|
import es from "./i18n/activityScoring.es.json";
|
|
@@ -34,6 +35,7 @@ function activityScorer(rules) {
|
|
|
34
35
|
}
|
|
35
36
|
const freshRefRescuesActivation = rules.freshRefRescuesActivation ?? true;
|
|
36
37
|
const tracksActivation = rules.tracksActivation ?? true;
|
|
38
|
+
const tracksOperators = rules.tracksOperators ?? true;
|
|
37
39
|
const allowsMultiple = rules.allowsMultipleReferences ?? false;
|
|
38
40
|
const activatesDaily = (rules.activates ?? "daily") === "daily";
|
|
39
41
|
const unique = new Set(rules.uniquePer ?? []);
|
|
@@ -79,11 +81,15 @@ function activityScorer(rules) {
|
|
|
79
81
|
return {
|
|
80
82
|
workedByCall: {},
|
|
81
83
|
activatedRefs: {},
|
|
84
|
+
operatorRefs: {},
|
|
85
|
+
guestOperators: false,
|
|
82
86
|
huntedRefs: {},
|
|
83
87
|
activatedQsos: 0,
|
|
84
88
|
huntedQsos: 0,
|
|
85
89
|
duplicates: 0,
|
|
86
90
|
dayActivatedRefs: {},
|
|
91
|
+
dayOperatorRefs: {},
|
|
92
|
+
dayGuestOperators: false,
|
|
87
93
|
dayHuntedRefs: {},
|
|
88
94
|
dayActivatedQsos: 0,
|
|
89
95
|
dayHuntedQsos: 0,
|
|
@@ -95,9 +101,14 @@ function activityScorer(rules) {
|
|
|
95
101
|
// per QSO makes a full pass quadratic. The harness copies any resume
|
|
96
102
|
// checkpoint at its boundary, so nothing cached is ever aliased.
|
|
97
103
|
scoreQso({ scoresheet, qso, operation, isNewDay }) {
|
|
104
|
+
var _a, _b;
|
|
98
105
|
const sheet = scoresheet;
|
|
106
|
+
sheet.operatorRefs ?? (sheet.operatorRefs = {});
|
|
107
|
+
sheet.dayOperatorRefs ?? (sheet.dayOperatorRefs = {});
|
|
99
108
|
if (isNewDay && activatesDaily) {
|
|
100
109
|
sheet.dayActivatedRefs = {};
|
|
110
|
+
sheet.dayOperatorRefs = {};
|
|
111
|
+
sheet.dayGuestOperators = false;
|
|
101
112
|
sheet.dayHuntedRefs = {};
|
|
102
113
|
sheet.dayActivatedQsos = 0;
|
|
103
114
|
sheet.dayHuntedQsos = 0;
|
|
@@ -136,9 +147,20 @@ function activityScorer(rules) {
|
|
|
136
147
|
sheet.dayDuplicates += 1;
|
|
137
148
|
return { scoresheet: sheet, score };
|
|
138
149
|
}
|
|
150
|
+
const operator = tracksOperators ? str((qso.our ?? {}).operatorCall)?.trim().toUpperCase() : void 0;
|
|
151
|
+
if (operator && isGuestOperator(operator, operation)) {
|
|
152
|
+
sheet.guestOperators = true;
|
|
153
|
+
sheet.dayGuestOperators = true;
|
|
154
|
+
}
|
|
139
155
|
for (const ref of activationRefs) {
|
|
140
156
|
sheet.activatedRefs[ref] = (sheet.activatedRefs[ref] ?? 0) + activationCredit;
|
|
141
157
|
sheet.dayActivatedRefs[ref] = (sheet.dayActivatedRefs[ref] ?? 0) + activationCredit;
|
|
158
|
+
if (operator) {
|
|
159
|
+
const shares = (_a = sheet.operatorRefs)[ref] ?? (_a[ref] = {});
|
|
160
|
+
shares[operator] = (shares[operator] ?? 0) + activationCredit;
|
|
161
|
+
const dayShares = (_b = sheet.dayOperatorRefs)[ref] ?? (_b[ref] = {});
|
|
162
|
+
dayShares[operator] = (dayShares[operator] ?? 0) + activationCredit;
|
|
163
|
+
}
|
|
142
164
|
}
|
|
143
165
|
sheet.activatedQsos += activationCredit;
|
|
144
166
|
sheet.dayActivatedQsos += activationCredit;
|
|
@@ -160,6 +182,7 @@ function activityScorer(rules) {
|
|
|
160
182
|
if (tracksActivation && Object.keys(activatedRefs).length > 0) {
|
|
161
183
|
tallies.activation = summarizeActivation(
|
|
162
184
|
activatedRefs,
|
|
185
|
+
(perDay ? scoresheet.dayGuestOperators : scoresheet.guestOperators) ? perDay ? scoresheet.dayOperatorRefs : scoresheet.operatorRefs : {},
|
|
163
186
|
perDay ? scoresheet.dayActivatedQsos : scoresheet.activatedQsos,
|
|
164
187
|
perDay ? scoresheet.dayDuplicates : scoresheet.duplicates,
|
|
165
188
|
huntedQsos,
|
|
@@ -176,7 +199,15 @@ function activityScorer(rules) {
|
|
|
176
199
|
}
|
|
177
200
|
};
|
|
178
201
|
}
|
|
179
|
-
function
|
|
202
|
+
function isGuestOperator(operator, operation) {
|
|
203
|
+
const stations = String(operation.stationCall ?? "").split(",").map((call) => baseCallOf(call)).filter((call) => call.length > 0);
|
|
204
|
+
return !stations.includes(baseCallOf(operator));
|
|
205
|
+
}
|
|
206
|
+
function baseCallOf(call) {
|
|
207
|
+
const trimmed = call.trim().toUpperCase();
|
|
208
|
+
return parseCallsign(trimmed).baseCall || trimmed;
|
|
209
|
+
}
|
|
210
|
+
function summarizeActivation(activatedRefs, operatorRefs, qsos, duplicates, huntedQsos, scope, qsosToActivate, rules, t, ctx) {
|
|
180
211
|
const refKeys = Object.keys(activatedRefs).sort();
|
|
181
212
|
const lowest = Math.min(...refKeys.map((key) => activatedRefs[key]));
|
|
182
213
|
const activated = lowest >= qsosToActivate;
|
|
@@ -184,9 +215,14 @@ function summarizeActivation(activatedRefs, qsos, duplicates, huntedQsos, scope,
|
|
|
184
215
|
if (!activated) summary = `${fmtInteger(lowest)}/${fmtInteger(qsosToActivate)}`;
|
|
185
216
|
else if (refKeys.length < 6) summary = `${fmtInteger(lowest)} ${"\u2713".repeat(refKeys.length)}`;
|
|
186
217
|
else summary = `${fmtInteger(lowest)} \u2713 x ${fmtInteger(refKeys.length)}`;
|
|
187
|
-
const detail = refKeys.
|
|
188
|
-
|
|
189
|
-
|
|
218
|
+
const detail = refKeys.flatMap((key) => {
|
|
219
|
+
const line = activatedRefs[key] >= qsosToActivate ? `\u2705 **${key}: ${fmtInteger(activatedRefs[key])}**` : `\u274C ${key}: ${fmtInteger(activatedRefs[key])}/${fmtInteger(qsosToActivate)}`;
|
|
220
|
+
const byOperator = operatorRefs[key] ?? {};
|
|
221
|
+
const operators = Object.keys(byOperator).sort();
|
|
222
|
+
if (operators.length === 0) return [line];
|
|
223
|
+
const shares = operators.map((op) => byOperator[op] >= qsosToActivate ? `${op} \u2713` : `${op} ${fmtInteger(byOperator[op])}/${fmtInteger(qsosToActivate)}`);
|
|
224
|
+
return [line, `\xA0\xA0\xA0\xA0${shares.join(" \xB7 ")}`];
|
|
225
|
+
});
|
|
190
226
|
const parts = [qsos === 1 ? t("activationQsoOne") : t("activationQsoMany", { formatted: fmtInteger(qsos) })];
|
|
191
227
|
if (huntedQsos > 0) {
|
|
192
228
|
const p2p = resolveRuleText(rules.p2pLabel, ctx, t("ref2ref"));
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @ham2k/extension-sdk 0.
|
|
1
|
+
// @ham2k/extension-sdk 0.8.0
|
|
2
2
|
/** Experimental v1 scene contract. All coordinates are in the scene's viewBox. */
|
|
3
3
|
export interface SvgScene {
|
|
4
4
|
version: 1;
|
|
@@ -1549,6 +1549,7 @@ export interface ActivityScoringRules {
|
|
|
1549
1549
|
huntingType?: string;
|
|
1550
1550
|
qsosToActivate?: number | ((activatedRefs: string[]) => number);
|
|
1551
1551
|
tracksActivation?: boolean;
|
|
1552
|
+
tracksOperators?: boolean;
|
|
1552
1553
|
freshRefRescuesActivation?: boolean;
|
|
1553
1554
|
allowsMultipleReferences?: boolean;
|
|
1554
1555
|
uniquePer?: readonly UniqueAxis[];
|
|
@@ -1560,11 +1561,15 @@ export interface ActivityScoringRules {
|
|
|
1560
1561
|
export type ActivityScoresheet = {
|
|
1561
1562
|
workedByCall: Record<string, string[]>;
|
|
1562
1563
|
activatedRefs: Record<string, number>;
|
|
1564
|
+
operatorRefs: Record<string, Record<string, number>>;
|
|
1565
|
+
guestOperators: boolean;
|
|
1563
1566
|
huntedRefs: Record<string, number>;
|
|
1564
1567
|
activatedQsos: number;
|
|
1565
1568
|
huntedQsos: number;
|
|
1566
1569
|
duplicates: number;
|
|
1567
1570
|
dayActivatedRefs: Record<string, number>;
|
|
1571
|
+
dayOperatorRefs: Record<string, Record<string, number>>;
|
|
1572
|
+
dayGuestOperators: boolean;
|
|
1568
1573
|
dayHuntedRefs: Record<string, number>;
|
|
1569
1574
|
dayActivatedQsos: number;
|
|
1570
1575
|
dayHuntedQsos: number;
|
package/docs/distribution.md
CHANGED
|
@@ -352,7 +352,9 @@ from whoever it claims; it is not worth it while a key is all there is.
|
|
|
352
352
|
a single confirmation that names each extension and the version it moves to,
|
|
353
353
|
and skips the per-extension screen for each of them: asking once per entry for
|
|
354
354
|
the one thing the operator asked for once is how a screen gets pressed through
|
|
355
|
-
unread.
|
|
355
|
+
unread. Pressed on the update notice when the notice's text already named every
|
|
356
|
+
extension it takes, it skips that confirmation too: the press under those names
|
|
357
|
+
was it. It is a confirmation of a job the operator started, never a way to
|
|
356
358
|
start one — `install_consent_scope_test` holds it to the one function that
|
|
357
359
|
shows that confirmation first.
|
|
358
360
|
|
|
@@ -492,7 +494,8 @@ At most once every six hours — on launch and on resume — the app asks the
|
|
|
492
494
|
catalog whether anything installed has a newer release, and says so in the
|
|
493
495
|
status bar and on the row; the notice comes down with the last update taken.
|
|
494
496
|
It never installs unasked: the notice's **Update all** is the panel's own,
|
|
495
|
-
behind the same confirmation
|
|
497
|
+
behind the same confirmation unless the notice named every extension it takes,
|
|
498
|
+
and **More info** opens the panel.
|
|
496
499
|
|
|
497
500
|
An install or update narrates itself as one status-bar progress item, from the
|
|
498
501
|
first downloaded byte to "Installed", and the Extensions panel mirrors that
|