feat(db): fill in the distances nothing ever recorded
The published page derives a distance as it renders, which fixed the empty column there but not the cause: the field is empty in the database, so it goes out empty in every ADIF export and leaves the same gap in whoever imports it. BackfillDistances computes it from the two locators for QSOs that have both. Only where it is EMPTY: a stored distance came from the log that recorded the contact, which knew the real positions, and two four-character squares are a worse answer that must not overwrite a better one. Whole kilometres, for the same reason the published column is. In the Database panel rather than beside the county backfill, which lives under US Counties: this one touches the whole logbook, whatever country the QSOs are in.
This commit is contained in:
@@ -45,7 +45,7 @@ import {
|
||||
TestLoTWUpload, ListTQSLStationLocations,
|
||||
DownloadLoTWUsers, GetLoTWUsersStatus,
|
||||
GetScpStatus, SetScpEnabled, DownloadScp,
|
||||
DownloadULSCounties, ULSStatus, BackfillUSCounties,
|
||||
DownloadULSCounties, ULSStatus, BackfillUSCounties, BackfillDistances,
|
||||
ComputeStationInfo,
|
||||
GetUIPref, SetUIPref,
|
||||
GetFlexState, GetFlexBandAntennas, SaveFlexBandAntennas, GetFlexBandPower, SaveFlexBandPower,
|
||||
@@ -1495,6 +1495,14 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
try { await DownloadULSCounties(); }
|
||||
catch (e: any) { setUlsBusy(false); setUlsProgress(null); setUlsMsg({ ok: false, text: String(e?.message ?? e) }); }
|
||||
};
|
||||
const [distBusy, setDistBusy] = useState(false);
|
||||
const [distMsg, setDistMsg] = useState<string | null>(null);
|
||||
const runDistBackfill = async () => {
|
||||
setDistBusy(true); setDistMsg(null);
|
||||
try { const r: any = await BackfillDistances(); setDistMsg(t("db.distDone", { f: r?.filled ?? 0, s: r?.scanned ?? 0, n: r?.no_grid ?? 0 })); }
|
||||
catch (e: any) { setDistMsg(String(e?.message ?? e)); }
|
||||
finally { setDistBusy(false); }
|
||||
};
|
||||
const runBackfill = async () => {
|
||||
setBackfillBusy(true); setBackfillMsg(null);
|
||||
try { const r: any = await BackfillUSCounties(); setBackfillMsg(t('uscty.backfillDone', { c: r?.county ?? 0, g: r?.grid ?? 0, s: r?.scanned ?? 0 })); }
|
||||
@@ -5323,6 +5331,21 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
<>
|
||||
<SectionHeader title={t('sec.database')} />
|
||||
|
||||
{/* Distance backfill. Here rather than beside the county one, which lives
|
||||
in the US Counties panel: this touches the whole logbook, whatever
|
||||
country the QSOs are in. */}
|
||||
<div className="rounded-md border border-border p-3 space-y-2 max-w-2xl mb-5">
|
||||
<div className="text-xs font-medium">{t('db.distTitle')}</div>
|
||||
<p className="text-[11px] text-muted-foreground leading-relaxed">{t('db.distIntro')}</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<Button size="sm" variant="secondary" onClick={runDistBackfill} disabled={distBusy}>
|
||||
{distBusy ? <Loader2 className="size-3.5 animate-spin mr-1.5" /> : null}
|
||||
{t('db.distRun')}
|
||||
</Button>
|
||||
{distMsg && <span className="text-xs text-muted-foreground">{distMsg}</span>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Settings / application database (settings + profiles) — always shown,
|
||||
distinct from the QSO logbook so the two are never confused. */}
|
||||
<div className="space-y-2 max-w-2xl mb-5 border border-border/60 rounded-md p-3">
|
||||
|
||||
@@ -151,7 +151,7 @@ const en: Dict = {
|
||||
'uscty.download': 'Download',
|
||||
'uscty.update': 'Update',
|
||||
'uscty.done': 'County database ready — {n} callsigns.',
|
||||
'uscty.backfillTitle': 'Fill existing QSOs',
|
||||
'db.distTitle': 'Fill in distances', 'db.distIntro': 'Nothing records a distance when a QSO is logged, so the field is empty in every ADIF you export. This works it out from the two locators, for QSOs that have both and no distance yet. A distance already recorded is left alone.', 'db.distRun': 'Fill distances', 'db.distDone': '{f} filled out of {s} scanned; {n} had no pair of locators.', 'uscty.backfillTitle': 'Fill existing QSOs',
|
||||
'uscty.backfillIntro': 'Resolve county (and grid) for US QSOs already in your log that are missing them. Existing values are kept — only blanks are filled.',
|
||||
'uscty.backfillRun': 'Fill missing counties',
|
||||
'uscty.backfillDone': '{s} US QSOs scanned · {c} counties, {g} grids filled.',
|
||||
@@ -578,7 +578,7 @@ const fr: Dict = {
|
||||
'uscty.download': 'Télécharger',
|
||||
'uscty.update': 'Mettre à jour',
|
||||
'uscty.done': 'Base des comtés prête — {n} indicatifs.',
|
||||
'uscty.backfillTitle': 'Compléter les QSO existants',
|
||||
'db.distTitle': 'Compléter les distances', 'db.distIntro': 'Rien n enregistre de distance quand un QSO est journalisé, le champ part donc vide dans chaque ADIF exporté. Ceci la déduit des deux locators, pour les QSO qui ont les deux et pas encore de distance. Une distance déjà enregistrée n est pas touchée.', 'db.distRun': 'Compléter les distances', 'db.distDone': '{f} complétés sur {s} examinés ; {n} sans les deux locators.', 'uscty.backfillTitle': 'Compléter les QSO existants',
|
||||
'uscty.backfillIntro': "Résout le comté (et le locator) pour les QSO US déjà dans ton log qui n'en ont pas. Les valeurs existantes sont conservées — seuls les vides sont remplis.",
|
||||
'uscty.backfillRun': 'Remplir les comtés manquants',
|
||||
'uscty.backfillDone': '{s} QSO US analysés · {c} comtés, {g} locators remplis.',
|
||||
|
||||
Vendored
+2
@@ -88,6 +88,8 @@ export function AwardRefsForQSOs(arg1:Array<number>):Promise<Record<number, Reco
|
||||
|
||||
export function AwardsFolder():Promise<string>;
|
||||
|
||||
export function BackfillDistances():Promise<main.BackfillDistancesResult>;
|
||||
|
||||
export function BackfillUSCounties():Promise<main.BackfillUSCountiesResult>;
|
||||
|
||||
export function BandSlotQSOs(arg1:string,arg2:number,arg3:string,arg4:string):Promise<Array<qso.QSO>>;
|
||||
|
||||
@@ -118,6 +118,10 @@ export function AwardsFolder() {
|
||||
return window['go']['main']['App']['AwardsFolder']();
|
||||
}
|
||||
|
||||
export function BackfillDistances() {
|
||||
return window['go']['main']['App']['BackfillDistances']();
|
||||
}
|
||||
|
||||
export function BackfillUSCounties() {
|
||||
return window['go']['main']['App']['BackfillUSCounties']();
|
||||
}
|
||||
|
||||
@@ -1931,6 +1931,22 @@ export namespace main {
|
||||
this.to = source["to"];
|
||||
}
|
||||
}
|
||||
export class BackfillDistancesResult {
|
||||
scanned: number;
|
||||
filled: number;
|
||||
no_grid: number;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new BackfillDistancesResult(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.scanned = source["scanned"];
|
||||
this.filled = source["filled"];
|
||||
this.no_grid = source["no_grid"];
|
||||
}
|
||||
}
|
||||
export class BackfillUSCountiesResult {
|
||||
scanned: number;
|
||||
county: number;
|
||||
|
||||
Reference in New Issue
Block a user