Files
award/CLAUDE.md
Joerg f7d62ed247 docs: update CLAUDE.md with DCL parser information
- Add ADIF Parser utility section (src/backend/utils/adif-parser.js)
- Add DCL Service section with API integration details
- Update Confirmation Systems section with DCL information
- Add ADIF Format section explaining field format and DCL-specific fields
- Update Recent Commits with DCL parser implementation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 11:50:33 +01:00

272 lines
8.8 KiB
Markdown

Default to using Bun instead of Node.js.
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
- Use `bun test` instead of `jest` or `vitest`
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
- Use `bunx <package> <command>` instead of `npx <package> <command>`
- Bun automatically loads .env, so don't use dotenv.
## APIs
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
- `Bun.redis` for Redis. Don't use `ioredis`.
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
- `WebSocket` is built-in. Don't use `ws`.
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
- Bun.$`ls` instead of execa.
## Testing
Use `bun test` to run tests.
```ts#index.test.ts
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});
```
## Frontend
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
Server:
```ts#index.ts
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
```
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
```html#index.html
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
```
With the following `frontend.tsx`:
```tsx#frontend.tsx
import React from "react";
import { createRoot } from "react-dom/client";
// import .css files directly and it works
import './index.css';
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);
```
Then, run index.ts
```sh
bun --hot ./index.ts
```
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
## Project: Quickawards by DJ7NT
Quickawards is a amateur radio award tracking application that calculates progress toward various awards based on QSO (contact) data.
### Award System Architecture
The award system is JSON-driven and located in `award-definitions/` directory. Each award has:
- `id`: Unique identifier (e.g., "dld", "dxcc")
- `name`: Display name
- `description`: Short description
- `caption`: Detailed explanation
- `category`: Award category ("dxcc", "darc", etc.)
- `rules`: Award calculation logic
### Award Rule Types
1. **`entity`**: Count unique entities (DXCC countries, states, grid squares)
- `entityType`: What to count ("dxcc", "state", "grid", "callsign")
- `target`: Number required for award
- `filters`: Optional filters (band, mode, etc.)
- `displayField`: Optional field to display
2. **`dok`**: Count unique DOK (DARC Ortsverband Kennung) combinations
- `target`: Number required
- `confirmationType`: "dcl" (DARC Community Logbook)
- Counts unique (DOK, band, mode) combinations
- Only DCL-confirmed QSOs count
3. **`points`**: Point-based awards
- `stations`: Array of {callsign, points}
- `target`: Points required
- `countMode`: "perStation", "perBandMode", or "perQso"
4. **`filtered`**: Filtered version of another award
- `baseRule`: The base entity rule
- `filters`: Additional filters to apply
5. **`counter`**: Count QSOs or callsigns
### Key Files
**Backend Award Service**: `src/backend/services/awards.service.js`
- `getAllAwards()`: Returns all available award definitions
- `calculateAwardProgress(userId, award, options)`: Main calculation function
- `calculateDOKAwardProgress(userId, award, options)`: DOK-specific calculation
- `calculatePointsAwardProgress(userId, award, options)`: Point-based calculation
- `getAwardEntityBreakdown(userId, awardId)`: Detailed entity breakdown
- `getAwardProgressDetails(userId, awardId)`: Progress with details
**Database Schema**: `src/backend/db/schema/index.js`
- QSO fields include: `darcDok`, `dclQslRstatus`, `dclQslRdate`
- DOK fields support DLD award tracking
- DCL confirmation fields separate from LoTW
**Award Definitions**: `award-definitions/*.json`
- Add new awards by creating JSON definition files
- Add filename to `loadAwardDefinitions()` file list in awards.service.js
**ADIF Parser**: `src/backend/utils/adif-parser.js`
- `parseADIF(adifData)`: Parse ADIF format into QSO records
- `parseDCLResponse(response)`: Parse DCL's JSON response format `{ "adif": "..." }`
- `normalizeBand(band)`: Standardize band names (80m, 40m, etc.)
- `normalizeMode(mode)`: Standardize mode names (CW, FT8, SSB, etc.)
- Used by both LoTW and DCL services for consistency
**DCL Service**: `src/backend/services/dcl.service.js`
- `fetchQSOsFromDCL(dclApiKey, sinceDate)`: Fetch from DCL API
- `parseDCLJSONResponse(jsonResponse)`: Parse example/test payloads
- `syncQSOs(userId, dclApiKey, sinceDate, jobId)`: Sync QSOs to database
- `getLastDCLQSLDate(userId)`: Get last QSL date for incremental sync
- Ready for when DCL publishes their API
### DLD Award Implementation (COMPLETED)
The DLD (Deutschland Diplom) award was recently implemented:
**Definition**: `award-definitions/dld.json`
```json
{
"id": "dld",
"name": "DLD",
"description": "Deutschland Diplom - Confirm 100 unique DOKs on different bands/modes",
"caption": "Contact and confirm stations with 100 unique DOKs (DARC Ortsverband Kennung) on different band/mode combinations.",
"category": "darc",
"rules": {
"type": "dok",
"target": 100,
"confirmationType": "dcl",
"displayField": "darcDok"
}
}
```
**Implementation Details**:
- Function: `calculateDOKAwardProgress()` in `src/backend/services/awards.service.js` (lines 173-268)
- Counts unique (DOK, band, mode) combinations
- Only DCL-confirmed QSOs count (`dclQslRstatus === 'Y'`)
- Each unique DOK on each unique band/mode counts separately
- Returns worked, confirmed counts and entity breakdowns
**Database Fields Used**:
- `darcDok`: DOK identifier (e.g., "F03", "P30", "G20")
- `band`: Band (e.g., "80m", "40m", "20m")
- `mode`: Mode (e.g., "CW", "SSB", "FT8")
- `dclQslRstatus`: DCL confirmation status ('Y' = confirmed)
- `dclQslRdate`: DCL confirmation date
**Documentation**: See `docs/DOCUMENTATION.md` for complete documentation including DLD award example.
### Adding New Awards
To add a new award:
1. Create JSON definition in `award-definitions/`
2. Add filename to `loadAwardDefinitions()` in `src/backend/services/awards.service.js`
3. If new rule type needed, add calculation function
4. Add type handling in `calculateAwardProgress()` switch statement
5. Add type handling in `getAwardEntityBreakdown()` if needed
6. Update documentation in `docs/DOCUMENTATION.md`
7. Test with sample QSO data
### Confirmation Systems
- **LoTW (Logbook of The World)**: ARRL's confirmation system
- Service: `src/backend/services/lotw.service.js`
- Fields: `lotwQslRstatus`, `lotwQslRdate`
- Used for DXCC, WAS, VUCC, most awards
- ADIF format with `<EOR>` delimiters
- Supports incremental sync by date
- **DCL (DARC Community Logbook)**: DARC's confirmation system
- Service: `src/backend/services/dcl.service.js`
- Fields: `dclQslRstatus`, `dclQslRdate`
- DOK fields: `darcDok` (partner's DOK), `myDarcDok` (user's DOK)
- Required for DLD award
- German amateur radio specific
- API in development (parser ready)
- Response format: JSON with ADIF string in `adif` field
- Supports DOK (DARC Ortsverband Kennung) data
### ADIF Format
Both LoTW and DCL return data in ADIF (Amateur Data Interchange Format):
- Field format: `<FIELD_NAME:length>value`
- Record delimiter: `<EOR>` (end of record)
- Header ends with: `<EOH>` (end of header)
- Example: `<CALL:5>DK0MU<BAND:3>80m<QSO_DATE:8>20250621<EOR>`
**DCL-specific fields**:
- `DCL_QSL_RCVD`: DCL confirmation status (Y/N/?)
- `DCL_QSLRDATE`: DCL confirmation date (YYYYMMDD)
- `DARC_DOK`: QSO partner's DOK
- `MY_DARC_DOK`: User's own DOK
- `STATION_CALLSIGN`: User's callsign
### Recent Commits
- `8a1a580`: feat: implement DCL ADIF parser and service integration
- Add shared ADIF parser utility (src/backend/utils/adif-parser.js)
- Implement DCL service with API integration ready
- Refactor LoTW service to use shared parser
- Tested with example DCL payload (6 QSOs parsed successfully)
- `c982dcd`: feat: implement DLD (Deutschland Diplom) award
- `322ccaf`: docs: add DLD (Deutschland Diplom) award documentation