Skip to content

Claude/issue 1116 plan 8b6btf - #1132

Merged
ea4k merged 11 commits into
masterfrom
claude/issue-1116-plan-8b6btf
Aug 21, 2026
Merged

Claude/issue 1116 plan 8b6btf#1132
ea4k merged 11 commits into
masterfrom
claude/issue-1116-plan-8b6btf

Conversation

@ea4k

@ea4k ea4k commented Aug 21, 2026

Copy link
Copy Markdown
Owner

No description provided.

claude and others added 11 commits August 20, 2026 07:48
DXCC = 0 is not a valid entity. The my_dxcc validation rule accepted
0 as valid, and for the relational dxcc column, QSqlRelationalTableModel
falls back to the raw foreign key (0) instead of an invalid value when
there is no matching row in "entity", so the "0" leaked through to the
logview instead of showing an empty cell.

Fixes #1128, part of #1116
Extends the same validation approach used for my_dxcc to my_fists,
fists, my_itu_zone, my_cq_zone, cqz, and ituz: these are plain integer
columns (no relation to another table), so an out-of-range or zero
value is hidden as a blank cell instead of showing a meaningless
number. CQ/ITU zone bounds match Adif::isValidCQz/isValidITUz.

Part of #1116
LogModel::s_validationRules duplicated numeric range knowledge that
Adif already owns (isValidCQz, isValidITUz, isValidDXCC, isValidAge,
isValidAnt_AZ, isValidFISTS), and had already drifted from it: my_dxcc
accepted up to 530 while Adif::isValidDXCC caps at 522. Mark those
Adif methods static (they are pure range checks with no instance
state) and have LogModel call them directly instead of duplicating
the bounds, so there is a single source of truth reused wherever
ADIF-domain validation is needed.

No upper bound was added for FISTS: unlike CQ/ITU zones or DXCC
entities, which validate against small, effectively fixed real-world
enumerations, FISTS membership numbers grow indefinitely, so a cap
tied to today's ~24k members would need bumping again in a couple of
years. Adif::isValidFISTS already only checks for a positive value.

Part of #1116
…nd FISTS_CC

Centralized in Adif wherever the check is a pure function of already-known
values, with static methods reused by both the logview and ADIF export:

- Adif::isValidAltitude (>0.0, replacing the ad hoc "!= 0" checks in
  QSO::getADIFStandard for ALTITUDE/MY_ALTITUDE).
- Adif::isValidSilentKey: only "Y" is shown/exported; blank/"N"/invalid
  are treated as no data.
- Adif::isValidQSORandom: only exported when explicitly false ("N"),
  resolving the long-standing "TODO: Check whether it makes sense..."
  left in QSO::getADIFStandard.
- Adif::isValidForceInit(forceInit, propMode): only valid together with
  PROP_MODE=EME. The previous export check ("if (forceInit)") never
  looked at PROP_MODE at all.
- Adif::isValidFreq (already existed, now static) reused for FreqRX in
  the logview; ADIF export already correctly skipped FREQ_RX when
  invalid or equal to FREQ_TX via the Frequency class, so it needed no
  change.
- Adif::isValidFISTS (already existed) is now also used for FISTS_CC in
  both the logview and export, instead of a separate ">0" duplicate.

FORCE_INIT needs the sibling PROP_MODE value, which a single-value
ValidationFunc entry in LogModel::s_validationRules cannot express, so
it is special-cased in LogModel::data() (same pattern already used for
band_rx), reading the row's prop_mode via the model's own index().

Part of #1116
QSO_COMPLETE defaults to "Y" and is stored in the DB as a numeric code
(1-4), not the ADIF Y/N/NIL/? string. The previous export check only
tested "!= Y" on the in-memory value, which QSO::setQSOComplete leaves
empty on invalid input, so an invalid value ("") still passed the
"!= Y" test and got exported as an empty QSO_COMPLETE field.

Adif::isValidQSOCompleteToExport (static) composes the existing
isValidQSO_COMPLETE enum check with "not Y", and is reused by both
QSO::getADIFStandard() and LogModel::data(). The logview needs a
special case (like force_init/band_rx) because it must first translate
the raw DB code to the ADIF value via Adif::getQSO_COMPLETEFromDB
(now static) before validating it, and shows that translated N/NIL/?
value instead of the raw meaningless integer.

Part of #1116
… export

qso_complete used to be stored as an internal numeric code (1=Y, 2=N,
3=NIL, 4=?), round-tripped through Adif::setQSO_COMPLETEToDB /
getQSO_COMPLETEFromDB on every write/read. Since every read/write path
already funneled through those two functions and nothing else depended
on the numeric encoding, the column now stores the ADIF value itself
(Y/N/NIL/?), which removes the round-trip entirely:

- database.cpp: createTableLog() declares qso_complete as VARCHAR(3).
  updateTo031() (still unreleased, so folded into the existing version
  bump rather than adding a new one) now also converts existing rows'
  numeric codes to the ADIF value after recreateTableLog() copies them
  in as text.
- dataproxy_sqlite.cpp: bind/read the ADIF string directly in
  getPreparedQuery/bindQSOValues/fromDB instead of encoding/decoding it.
- Adif::setQSO_COMPLETEToDB / getQSO_COMPLETEFromDB are removed; nothing
  needs them any more.
- LogModel's qso_complete now uses the regular s_validationRules entry
  like the other fields, instead of a special case that had to decode
  the numeric DB value before validating it.

Bug fix found and corrected in the process: in
DataProxy_SQLite::getADIFFromQSOQuery() (the ADIF-export reconstruction
path), the decoded QSO_COMPLETE value was discarded -- the line that
should have called qso.setQSOComplete(...) was commented out -- so
ADIF export always used the default "Y" regardless of the QSO's actual
value. Reading the (now plain-string) DB value directly and passing it
to setQSOComplete() fixes this as a side effect of the simplification.

Part of #1116
…tMode(0)

MainQSOEntryWidget::selectDefaultMode()'s fallback (used when the log has
no QSOs yet, e.g. on a fresh install, so getMostUsedMode() returns no
result) passed a band combo item into getIdFromModeName(), which looks up
submodes: a band name (e.g. "10m") never matches, so the mode ended up
unresolved. MainWindow::selectDefaultMode() already does this correctly
with getMode(0); this mirrors it.

Part of #1116
…start the WSJT-X UDP server

Investigated all 9 requested defaults against MainWindow::loadSettings()'s
settings.value(key, fallback) calls, which is what actually governs
behavior on a truly fresh install (independent of whether the Setup
dialog is ever opened). 7 of the 9 already matched the requested default
(UTC time, real time, seconds hidden, mark QSL pending on receipt, mark
eQSL/LoTW as queued on new QSO, check for new versions, show worked
callsign in search) - no change needed for those.

Only two did not:
- IncludeModeForNeeded: fallback was false; awards/needed-confirmed logic
  now considers band+mode by default, not just band.
- UDPServer: fallback was false; the WSJT-X UDP server now starts
  automatically by default.

Also updated SetupDialog::setDefaults() (used the first time the Setup
dialog itself runs) to match: added the missing setShowSeconds(false)
and setIncludeModeForNeeded(true) calls, and flipped setUDPServer to
true, so the dialog's own checkboxes reflect the same defaults.
SetupDialog::slotReadConfigData() calls setDefaults() then unconditionally
calls loadSettings() right after, on every first run. Each SetupPage's own
loadSettings() re-reads straight from QSettings, and most of these reads
had no fallback default, so on a fresh install (empty ini file) they always
came back false/unchecked regardless of what setDefaults() had just set in
memory - visible in the Setup dialog screenshot where every checkbox was
unchecked except the one call that already had a "true" fallback
(SendEQSLByDefault).

Added the matching default to each settings.value(...) read in
SetupPageMisc::loadSettings() and SetupPageUDP::loadSettings() for the
options that should default to on: RealTime, UTCTime, SendQSLWhenRec,
ShowCallsignInSearch, CheckNewVersions, IncludeModeForNeeded, and
UDPServer (ShowSeconds keeps its false default, matching the earlier
request to leave seconds hidden).

This is the actual mechanism that governs what a fresh install shows,
superseding the SetupDialog::setDefaults() and MainWindow::loadSettings()
fallback edits from the previous commit, which only affected in-memory
state that got immediately overwritten.
@ea4k
ea4k merged commit 82ac6a5 into master Aug 21, 2026
2 of 8 checks passed
@ea4k
ea4k deleted the claude/issue-1116-plan-8b6btf branch August 21, 2026 11:35
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
B Maintainability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants