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 <noreply@anthropic.com>
This commit is contained in:
2026-01-18 07:30:15 +01:00
parent 720144627e
commit 7f77c3adc9
5 changed files with 91 additions and 3 deletions

View File

@@ -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" }
]
}
}
}

View File

@@ -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" }
]
}
}
}

View File

@@ -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" }
]
}
}
}

View File

@@ -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" }
]
}
}
}

View File

@@ -27,6 +27,10 @@ function loadAwardDefinitions() {
'sat-rs44.json', 'sat-rs44.json',
'special-stations.json', 'special-stations.json',
'dld.json', 'dld.json',
'dld-80m.json',
'dld-40m.json',
'dld-cw.json',
'dld-80m-cw.json',
]; ];
for (const file of files) { for (const file of files) {
@@ -173,9 +177,9 @@ export async function calculateAwardProgress(userId, award, options = {}) {
async function calculateDOKAwardProgress(userId, award, options = {}) { async function calculateDOKAwardProgress(userId, award, options = {}) {
const { includeDetails = false } = options; const { includeDetails = false } = options;
const { rules } = award; 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 // Get all QSOs for user
const allQSOs = await db const allQSOs = await db
@@ -185,10 +189,17 @@ async function calculateDOKAwardProgress(userId, award, options = {}) {
logger.debug('Total QSOs for user', { count: allQSOs.length }); 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 // Track unique (DOK, band, mode) combinations
const dokCombinations = new Map(); // Key: "DOK/band/mode" -> detail object const dokCombinations = new Map(); // Key: "DOK/band/mode" -> detail object
for (const qso of allQSOs) { for (const qso of filteredQSOs) {
const dok = qso.darcDok; const dok = qso.darcDok;
if (!dok) continue; // Skip QSOs without DOK if (!dok) continue; // Skip QSOs without DOK