From 7f77c3adc9abdf43e76e03dad5dbc2eee985c887 Mon Sep 17 00:00:00 2001 From: Joerg Date: Sun, 18 Jan 2026 07:30:15 +0100 Subject: [PATCH] feat: add filter support for DOK awards The DOK award type (used for DLD award) now supports filtering by band, mode, and other QSO fields. This allows creating award variants like: - DLD on specific bands (80m, 40m, etc.) - DLD on specific modes (CW, SSB, etc.) - DLD with combined filters (e.g., 80m + CW) Changes: - Modified calculateDOKAwardProgress() to apply filters before processing - Added example awards: dld-80m, dld-40m, dld-cw, dld-80m-cw - Filter system uses existing applyFilters() function Co-Authored-By: Claude Sonnet 4.5 --- award-definitions/dld-40m.json | 19 +++++++++++++++++++ award-definitions/dld-80m-cw.json | 20 ++++++++++++++++++++ award-definitions/dld-80m.json | 19 +++++++++++++++++++ award-definitions/dld-cw.json | 19 +++++++++++++++++++ src/backend/services/awards.service.js | 17 ++++++++++++++--- 5 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 award-definitions/dld-40m.json create mode 100644 award-definitions/dld-80m-cw.json create mode 100644 award-definitions/dld-80m.json create mode 100644 award-definitions/dld-cw.json diff --git a/award-definitions/dld-40m.json b/award-definitions/dld-40m.json new file mode 100644 index 0000000..df172c9 --- /dev/null +++ b/award-definitions/dld-40m.json @@ -0,0 +1,19 @@ +{ + "id": "dld-40m", + "name": "DLD 40m", + "description": "Confirm 100 unique DOKs on 40m", + "caption": "Contact and confirm stations with 100 unique DOKs (DARC Ortsverband Kennung) on the 40m band. Only DCL-confirmed QSOs with valid DOK information on 40m count toward this award.", + "category": "darc", + "rules": { + "type": "dok", + "target": 100, + "confirmationType": "dcl", + "displayField": "darcDok", + "filters": { + "operator": "AND", + "filters": [ + { "field": "band", "operator": "eq", "value": "40m" } + ] + } + } +} diff --git a/award-definitions/dld-80m-cw.json b/award-definitions/dld-80m-cw.json new file mode 100644 index 0000000..895b7f4 --- /dev/null +++ b/award-definitions/dld-80m-cw.json @@ -0,0 +1,20 @@ +{ + "id": "dld-80m-cw", + "name": "DLD 80m CW", + "description": "Confirm 100 unique DOKs on 80m using CW", + "caption": "Contact and confirm stations with 100 unique DOKs (DARC Ortsverband Kennung) on the 80m band using CW mode. Only DCL-confirmed QSOs with valid DOK information on 80m CW count toward this award.", + "category": "darc", + "rules": { + "type": "dok", + "target": 100, + "confirmationType": "dcl", + "displayField": "darcDok", + "filters": { + "operator": "AND", + "filters": [ + { "field": "band", "operator": "eq", "value": "80m" }, + { "field": "mode", "operator": "eq", "value": "CW" } + ] + } + } +} diff --git a/award-definitions/dld-80m.json b/award-definitions/dld-80m.json new file mode 100644 index 0000000..cd47894 --- /dev/null +++ b/award-definitions/dld-80m.json @@ -0,0 +1,19 @@ +{ + "id": "dld-80m", + "name": "DLD 80m", + "description": "Confirm 100 unique DOKs on 80m", + "caption": "Contact and confirm stations with 100 unique DOKs (DARC Ortsverband Kennung) on the 80m band. Only DCL-confirmed QSOs with valid DOK information on 80m count toward this award.", + "category": "darc", + "rules": { + "type": "dok", + "target": 100, + "confirmationType": "dcl", + "displayField": "darcDok", + "filters": { + "operator": "AND", + "filters": [ + { "field": "band", "operator": "eq", "value": "80m" } + ] + } + } +} diff --git a/award-definitions/dld-cw.json b/award-definitions/dld-cw.json new file mode 100644 index 0000000..9288367 --- /dev/null +++ b/award-definitions/dld-cw.json @@ -0,0 +1,19 @@ +{ + "id": "dld-cw", + "name": "DLD CW", + "description": "Confirm 100 unique DOKs using CW mode", + "caption": "Contact and confirm stations with 100 unique DOKs (DARC Ortsverband Kennung) using CW (Morse code). Each unique DOK on CW counts separately. Only DCL-confirmed QSOs with valid DOK information count toward this award.", + "category": "darc", + "rules": { + "type": "dok", + "target": 100, + "confirmationType": "dcl", + "displayField": "darcDok", + "filters": { + "operator": "AND", + "filters": [ + { "field": "mode", "operator": "eq", "value": "CW" } + ] + } + } +} diff --git a/src/backend/services/awards.service.js b/src/backend/services/awards.service.js index 0423bcd..cd20d1b 100644 --- a/src/backend/services/awards.service.js +++ b/src/backend/services/awards.service.js @@ -27,6 +27,10 @@ function loadAwardDefinitions() { 'sat-rs44.json', 'special-stations.json', 'dld.json', + 'dld-80m.json', + 'dld-40m.json', + 'dld-cw.json', + 'dld-80m-cw.json', ]; for (const file of files) { @@ -173,9 +177,9 @@ export async function calculateAwardProgress(userId, award, options = {}) { async function calculateDOKAwardProgress(userId, award, options = {}) { const { includeDetails = false } = options; const { rules } = award; - const { target, displayField } = rules; + const { target, displayField, filters } = rules; - logger.debug('Calculating DOK-based award progress', { userId, awardId: award.id, target }); + logger.debug('Calculating DOK-based award progress', { userId, awardId: award.id, target, hasFilters: !!filters }); // Get all QSOs for user const allQSOs = await db @@ -185,10 +189,17 @@ async function calculateDOKAwardProgress(userId, award, options = {}) { logger.debug('Total QSOs for user', { count: allQSOs.length }); + // Apply filters if defined + let filteredQSOs = allQSOs; + if (filters) { + filteredQSOs = applyFilters(allQSOs, filters); + logger.debug('QSOs after DOK award filters', { count: filteredQSOs.length }); + } + // Track unique (DOK, band, mode) combinations const dokCombinations = new Map(); // Key: "DOK/band/mode" -> detail object - for (const qso of allQSOs) { + for (const qso of filteredQSOs) { const dok = qso.darcDok; if (!dok) continue; // Skip QSOs without DOK