perf(lotw): stop asking for the QSL details by default
A tester compared LogHX's two download options on the same account: two minutes for the confirmations, twenty for the confirmations with QSO details. OpsLog was always sending qso_qsldetail=yes — LogHX's slow option — which is the whole of the wait people were reporting. Marking a confirmation needs call, date, band and mode; the detail adds the QSL date and the station's grid, state and county. Now a choice, off by default, and forced on when the download is also ADDING the QSOs it cannot find — the one case where those fields have no other source.
This commit is contained in:
@@ -402,6 +402,7 @@ const (
|
|||||||
|
|
||||||
keyExtLoTWTQSLPath = "extsvc.lotw.tqsl_path"
|
keyExtLoTWTQSLPath = "extsvc.lotw.tqsl_path"
|
||||||
keyExtLoTWStationLoc = "extsvc.lotw.station_location"
|
keyExtLoTWStationLoc = "extsvc.lotw.station_location"
|
||||||
|
keyExtLoTWQSLDetail = "extsvc.lotw.qsl_detail" // ask LoTW for the QSL dates and station details (an order of magnitude slower)
|
||||||
keyExtLoTWAllCalls = "extsvc.lotw.download_all_calls" // download confirmations for EVERY call on the account, not just this profile's
|
keyExtLoTWAllCalls = "extsvc.lotw.download_all_calls" // download confirmations for EVERY call on the account, not just this profile's
|
||||||
keyExtLoTWForceCall = "extsvc.lotw.force_station_callsign" // override STATION_CALLSIGN at sign time (e.g. F4BPO/P on the F4BPO cert)
|
keyExtLoTWForceCall = "extsvc.lotw.force_station_callsign" // override STATION_CALLSIGN at sign time (e.g. F4BPO/P on the F4BPO cert)
|
||||||
keyExtLoTWKeyPassword = "extsvc.lotw.key_password"
|
keyExtLoTWKeyPassword = "extsvc.lotw.key_password"
|
||||||
@@ -12097,6 +12098,16 @@ func manualRefFor(existing, code string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLoTWQSLDetail reports whether the download asks LoTW for the QSL detail.
|
||||||
|
func (a *App) GetLoTWQSLDetail() bool {
|
||||||
|
return a.settingOr(keyExtLoTWQSLDetail, "") == "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLoTWQSLDetail stores that choice.
|
||||||
|
func (a *App) SetLoTWQSLDetail(on bool) {
|
||||||
|
a.setSetting(keyExtLoTWQSLDetail, map[bool]string{true: "1", false: "0"}[on])
|
||||||
|
}
|
||||||
|
|
||||||
// GetLoTWDownloadAllCalls reports whether the LoTW download ignores the
|
// GetLoTWDownloadAllCalls reports whether the LoTW download ignores the
|
||||||
// profile's own call and pulls every callsign on the account.
|
// profile's own call and pulls every callsign on the account.
|
||||||
func (a *App) GetLoTWDownloadAllCalls() bool {
|
func (a *App) GetLoTWDownloadAllCalls() bool {
|
||||||
@@ -12215,7 +12226,14 @@ func (a *App) runDownloadConfirmations(ctx context.Context, svc extsvc.Service,
|
|||||||
// The report arrives over minutes, and a window that says nothing while it
|
// The report arrives over minutes, and a window that says nothing while it
|
||||||
// does is indistinguishable from one that has hung — which is what it was
|
// does is indistinguishable from one that has hung — which is what it was
|
||||||
// being reported as. Every half-megabyte, say how much has landed.
|
// being reported as. Every half-megabyte, say how much has landed.
|
||||||
adifText, err := extsvc.DownloadLoTWConfirmations(ctx, nil, cfg.LoTW, sinceDate, ownCall, emit)
|
// Adding the QSOs LoTW knows and we do not is the one job that needs the
|
||||||
|
// slow report: without the detail those records would come in with no
|
||||||
|
// grid, state or county, and nothing else would ever fill them.
|
||||||
|
detail := addNotFound || a.settingOr(keyExtLoTWQSLDetail, "") == "1"
|
||||||
|
if detail {
|
||||||
|
emit("Asking for the QSL details too — LoTW takes considerably longer to build that report.")
|
||||||
|
}
|
||||||
|
adifText, err := extsvc.DownloadLoTWConfirmations(ctx, nil, cfg.LoTW, sinceDate, ownCall, detail, emit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
emit("Download failed: " + err.Error())
|
emit("Download failed: " + err.Error())
|
||||||
done(matched, total)
|
done(matched, total)
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "0.26.22",
|
||||||
|
"date": "",
|
||||||
|
"en": [
|
||||||
|
"LoTW download: the QSL details (QSL date, grid, state, county) are now optional and off by default — LoTW takes about ten times longer to build that report, twenty minutes against two on the same account, and marking a confirmation needs none of it. Still asked for automatically when adding the QSOs not found in the log."
|
||||||
|
],
|
||||||
|
"fr": [
|
||||||
|
"Téléchargement LoTW : les détails QSL (date du QSL, locator, état, comté) deviennent optionnels et désactivés par défaut — LoTW met environ dix fois plus longtemps à construire ce rapport, vingt minutes contre deux sur le même compte, et marquer une confirmation n'en a pas besoin. Toujours demandés automatiquement quand on ajoute les QSO absents du log."
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.26.21",
|
"version": "0.26.21",
|
||||||
"date": "",
|
"date": "",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
|
Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { GetLoTWDownloadAllCalls, SetLoTWDownloadAllCalls, OpenExternalURL, FindQSOsForUpload, UploadQSOsManual, DownloadConfirmations, CancelConfirmations, ImportHamlogConfirmations, ExportHamlogUnmatched, OpenADIFFile, SaveADIFFile, SyncPOTAHunterLog, ListQSO, BulkUpdateQSL, UploadCallsign, GetSlotStats } from '../../wailsjs/go/main/App';
|
import { GetLoTWQSLDetail, SetLoTWQSLDetail, GetLoTWDownloadAllCalls, SetLoTWDownloadAllCalls, OpenExternalURL, FindQSOsForUpload, UploadQSOsManual, DownloadConfirmations, CancelConfirmations, ImportHamlogConfirmations, ExportHamlogUnmatched, OpenADIFFile, SaveADIFFile, SyncPOTAHunterLog, ListQSO, BulkUpdateQSL, UploadCallsign, GetSlotStats } from '../../wailsjs/go/main/App';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { RecentQSOsGrid } from '@/components/RecentQSOsGrid';
|
import { RecentQSOsGrid } from '@/components/RecentQSOsGrid';
|
||||||
import { EventsOn } from '../../wailsjs/runtime/runtime';
|
import { EventsOn } from '../../wailsjs/runtime/runtime';
|
||||||
@@ -257,7 +257,13 @@ export function QSLManagerPanel({ onEditQSO, actions, paperRequest }: {
|
|||||||
const [addNotFound, setAddNotFound] = useState(false);
|
const [addNotFound, setAddNotFound] = useState(false);
|
||||||
// LoTW only: pull the whole account rather than this profile's callsign.
|
// LoTW only: pull the whole account rather than this profile's callsign.
|
||||||
const [lotwAllCalls, setLotwAllCalls] = useState(false);
|
const [lotwAllCalls, setLotwAllCalls] = useState(false);
|
||||||
useEffect(() => { GetLoTWDownloadAllCalls().then((v: boolean) => setLotwAllCalls(!!v)).catch(() => {}); }, []);
|
// LoTW only: ask for the QSL dates and station details. Ten times slower to
|
||||||
|
// build, so it is a choice rather than the default it used to be.
|
||||||
|
const [lotwDetail, setLotwDetail] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
GetLoTWDownloadAllCalls().then((v: boolean) => setLotwAllCalls(!!v)).catch(() => {});
|
||||||
|
GetLoTWQSLDetail().then((v: boolean) => setLotwDetail(!!v)).catch(() => {});
|
||||||
|
}, []);
|
||||||
// Download date window: 'last' = incremental since last pull, 'date' = from a
|
// Download date window: 'last' = incremental since last pull, 'date' = from a
|
||||||
// chosen date, 'all' = everything.
|
// chosen date, 'all' = everything.
|
||||||
const [sinceMode, setSinceMode] = useState<'last' | 'date' | 'all'>('last');
|
const [sinceMode, setSinceMode] = useState<'last' | 'date' | 'all'>('last');
|
||||||
@@ -751,6 +757,13 @@ export function QSLManagerPanel({ onEditQSO, actions, paperRequest }: {
|
|||||||
<Checkbox checked={addNotFound} onCheckedChange={(c) => setAddNotFound(!!c)} />
|
<Checkbox checked={addNotFound} onCheckedChange={(c) => setAddNotFound(!!c)} />
|
||||||
{t('qslm.addNotFound')}
|
{t('qslm.addNotFound')}
|
||||||
</label>
|
</label>
|
||||||
|
{service === 'lotw' && (
|
||||||
|
<label className="flex items-center gap-1.5 text-[11px] text-muted-foreground cursor-pointer" title={t('qslm.lotwDetailTitle')}>
|
||||||
|
<Checkbox checked={lotwDetail || addNotFound} disabled={addNotFound}
|
||||||
|
onCheckedChange={(c) => { setLotwDetail(!!c); SetLoTWQSLDetail(!!c); }} />
|
||||||
|
{t('qslm.lotwDetail')}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
{service === 'lotw' && (
|
{service === 'lotw' && (
|
||||||
<label className="flex items-center gap-1.5 text-[11px] text-muted-foreground cursor-pointer" title={t('qslm.lotwAllCallsTitle')}>
|
<label className="flex items-center gap-1.5 text-[11px] text-muted-foreground cursor-pointer" title={t('qslm.lotwAllCallsTitle')}>
|
||||||
<Checkbox checked={lotwAllCalls} onCheckedChange={(c) => { setLotwAllCalls(!!c); SetLoTWDownloadAllCalls(!!c); }} />
|
<Checkbox checked={lotwAllCalls} onCheckedChange={(c) => { setLotwAllCalls(!!c); SetLoTWDownloadAllCalls(!!c); }} />
|
||||||
|
|||||||
@@ -130,6 +130,8 @@ const en: Dict = {
|
|||||||
'icmp.scopeNoStream': 'This radio does not send its scope over CI-V — its own screen still works.',
|
'icmp.scopeNoStream': 'This radio does not send its scope over CI-V — its own screen still works.',
|
||||||
'gen.miles': 'Distances in miles', 'gen.milesHint': '(instead of kilometres)',
|
'gen.miles': 'Distances in miles', 'gen.milesHint': '(instead of kilometres)',
|
||||||
'qslm.qrzTitle': 'Open this callsign on QRZ.com',
|
'qslm.qrzTitle': 'Open this callsign on QRZ.com',
|
||||||
|
'qslm.lotwDetail': 'QSL details',
|
||||||
|
'qslm.lotwDetailTitle': 'Ask LoTW for the QSL date and the station details (grid, state, county) as well as the confirmation. LoTW takes about ten times longer to build that report — two minutes against twenty on the same account — and marking a confirmation needs none of it. Forced on when adding the QSOs not found, which have no other source for those fields.',
|
||||||
'qslm.lotwAllCalls': 'All my callsigns',
|
'qslm.lotwAllCalls': 'All my callsigns',
|
||||||
'qslm.lotwAllCallsTitle': "Download the confirmations of every callsign on the LoTW account, not just this profile's. A QSO made as F4BPO/P or TM2Q is confirmed at LoTW but never reaches an F4BPO profile without this.",
|
'qslm.lotwAllCallsTitle': "Download the confirmations of every callsign on the LoTW account, not just this profile's. A QSO made as F4BPO/P or TM2Q is confirmed at LoTW but never reaches an F4BPO profile without this.",
|
||||||
'awp.filterSlotsNotCfmd': 'Slots to confirm', 'awp.slotGap': 'slots to confirm',
|
'awp.filterSlotsNotCfmd': 'Slots to confirm', 'awp.slotGap': 'slots to confirm',
|
||||||
@@ -626,6 +628,8 @@ const fr: Dict = {
|
|||||||
'icmp.scopeNoStream': "Cette radio n'envoie pas son scope en CI-V — son propre écran fonctionne toujours.",
|
'icmp.scopeNoStream': "Cette radio n'envoie pas son scope en CI-V — son propre écran fonctionne toujours.",
|
||||||
'gen.miles': 'Distances en miles', 'gen.milesHint': '(au lieu des kilomètres)',
|
'gen.miles': 'Distances en miles', 'gen.milesHint': '(au lieu des kilomètres)',
|
||||||
'qslm.qrzTitle': 'Ouvrir cet indicatif sur QRZ.com',
|
'qslm.qrzTitle': 'Ouvrir cet indicatif sur QRZ.com',
|
||||||
|
'qslm.lotwDetail': 'Détails QSL',
|
||||||
|
'qslm.lotwDetailTitle': "Demander à LoTW la date du QSL et les détails de la station (locator, état, comté) en plus de la confirmation. LoTW met environ dix fois plus longtemps à construire ce rapport — deux minutes contre vingt sur le même compte — et marquer une confirmation n'en a pas besoin. Forcé quand on ajoute les QSO absents, qui n'ont pas d'autre source pour ces champs.",
|
||||||
'qslm.lotwAllCalls': 'Tous mes indicatifs',
|
'qslm.lotwAllCalls': 'Tous mes indicatifs',
|
||||||
'qslm.lotwAllCallsTitle': "Télécharger les confirmations de tous les indicatifs du compte LoTW, pas seulement celui du profil. Un QSO fait en F4BPO/P ou TM2Q est confirmé chez LoTW mais n'atteint jamais un profil F4BPO sans cette option.",
|
'qslm.lotwAllCallsTitle': "Télécharger les confirmations de tous les indicatifs du compte LoTW, pas seulement celui du profil. Un QSO fait en F4BPO/P ou TM2Q est confirmé chez LoTW mais n'atteint jamais un profil F4BPO sans cette option.",
|
||||||
'awp.filterSlotsNotCfmd': 'Slots à confirmer', 'awp.slotGap': 'slots à confirmer',
|
'awp.filterSlotsNotCfmd': 'Slots à confirmer', 'awp.slotGap': 'slots à confirmer',
|
||||||
|
|||||||
Vendored
+4
@@ -505,6 +505,8 @@ export function GetLiveStations():Promise<Array<main.LiveStation>>;
|
|||||||
|
|
||||||
export function GetLoTWDownloadAllCalls():Promise<boolean>;
|
export function GetLoTWDownloadAllCalls():Promise<boolean>;
|
||||||
|
|
||||||
|
export function GetLoTWQSLDetail():Promise<boolean>;
|
||||||
|
|
||||||
export function GetLoTWUsersStatus():Promise<main.LoTWUsersStatus>;
|
export function GetLoTWUsersStatus():Promise<main.LoTWUsersStatus>;
|
||||||
|
|
||||||
export function GetLogFilePath():Promise<string>;
|
export function GetLogFilePath():Promise<string>;
|
||||||
@@ -1161,6 +1163,8 @@ export function SetLinkedAmps(arg1:Array<string>):Promise<void>;
|
|||||||
|
|
||||||
export function SetLoTWDownloadAllCalls(arg1:boolean):Promise<void>;
|
export function SetLoTWDownloadAllCalls(arg1:boolean):Promise<void>;
|
||||||
|
|
||||||
|
export function SetLoTWQSLDetail(arg1:boolean):Promise<void>;
|
||||||
|
|
||||||
export function SetMotorFollow(arg1:boolean,arg2:number,arg3:string):Promise<void>;
|
export function SetMotorFollow(arg1:boolean,arg2:number,arg3:string):Promise<void>;
|
||||||
|
|
||||||
export function SetOpsLogQSLReceived(arg1:number,arg2:boolean):Promise<void>;
|
export function SetOpsLogQSLReceived(arg1:number,arg2:boolean):Promise<void>;
|
||||||
|
|||||||
@@ -950,6 +950,10 @@ export function GetLoTWDownloadAllCalls() {
|
|||||||
return window['go']['main']['App']['GetLoTWDownloadAllCalls']();
|
return window['go']['main']['App']['GetLoTWDownloadAllCalls']();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetLoTWQSLDetail() {
|
||||||
|
return window['go']['main']['App']['GetLoTWQSLDetail']();
|
||||||
|
}
|
||||||
|
|
||||||
export function GetLoTWUsersStatus() {
|
export function GetLoTWUsersStatus() {
|
||||||
return window['go']['main']['App']['GetLoTWUsersStatus']();
|
return window['go']['main']['App']['GetLoTWUsersStatus']();
|
||||||
}
|
}
|
||||||
@@ -2262,6 +2266,10 @@ export function SetLoTWDownloadAllCalls(arg1) {
|
|||||||
return window['go']['main']['App']['SetLoTWDownloadAllCalls'](arg1);
|
return window['go']['main']['App']['SetLoTWDownloadAllCalls'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function SetLoTWQSLDetail(arg1) {
|
||||||
|
return window['go']['main']['App']['SetLoTWQSLDetail'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function SetMotorFollow(arg1, arg2, arg3) {
|
export function SetMotorFollow(arg1, arg2, arg3) {
|
||||||
return window['go']['main']['App']['SetMotorFollow'](arg1, arg2, arg3);
|
return window['go']['main']['App']['SetMotorFollow'](arg1, arg2, arg3);
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-3
@@ -89,7 +89,7 @@ func readWithProgress(ctx context.Context, r io.Reader, note func(string)) ([]by
|
|||||||
// non-empty, only confirmations for that station callsign are returned (an
|
// non-empty, only confirmations for that station callsign are returned (an
|
||||||
// LoTW account holds every call you operate — F4BPO, F4BPO/P, TM2Q — so this
|
// LoTW account holds every call you operate — F4BPO, F4BPO/P, TM2Q — so this
|
||||||
// scopes the pull to the active profile's call).
|
// scopes the pull to the active profile's call).
|
||||||
func DownloadLoTWConfirmations(ctx context.Context, client *http.Client, cfg ServiceConfig, since, ownCall string, note func(string)) (string, error) {
|
func DownloadLoTWConfirmations(ctx context.Context, client *http.Client, cfg ServiceConfig, since, ownCall string, detail bool, note func(string)) (string, error) {
|
||||||
user := strings.TrimSpace(cfg.Username)
|
user := strings.TrimSpace(cfg.Username)
|
||||||
if user == "" || cfg.Password == "" {
|
if user == "" || cfg.Password == "" {
|
||||||
return "", fmt.Errorf("lotw: website login (username/password) not set")
|
return "", fmt.Errorf("lotw: website login (username/password) not set")
|
||||||
@@ -99,7 +99,18 @@ func DownloadLoTWConfirmations(ctx context.Context, client *http.Client, cfg Ser
|
|||||||
q.Set("password", cfg.Password)
|
q.Set("password", cfg.Password)
|
||||||
q.Set("qso_query", "1")
|
q.Set("qso_query", "1")
|
||||||
q.Set("qso_qsl", "yes") // only QSLed (confirmed) records
|
q.Set("qso_qsl", "yes") // only QSLed (confirmed) records
|
||||||
q.Set("qso_qsldetail", "yes") // include QSL_RCVD / QSLRDATE detail
|
// qso_qsldetail is what LoTW charges for: it adds the QSL date and the
|
||||||
|
// station's own DXCC / grid / state / county to every record, and takes an
|
||||||
|
// order of magnitude longer to build — a report that arrives in two minutes
|
||||||
|
// without it takes twenty with it, measured on the same account.
|
||||||
|
//
|
||||||
|
// What we actually need to mark a confirmation is call, date, band and mode.
|
||||||
|
// The rest is worth its price only when the download is also ADDING the QSOs
|
||||||
|
// it cannot find, which is the one case where the extra fields are the only
|
||||||
|
// source for them.
|
||||||
|
if detail {
|
||||||
|
q.Set("qso_qsldetail", "yes")
|
||||||
|
}
|
||||||
if c := strings.TrimSpace(ownCall); c != "" {
|
if c := strings.TrimSpace(ownCall); c != "" {
|
||||||
q.Set("qso_owncall", c) // restrict to this station callsign
|
q.Set("qso_owncall", c) // restrict to this station callsign
|
||||||
}
|
}
|
||||||
@@ -507,7 +518,7 @@ func TestLoTW(cfg ServiceConfig, stationDataPath string) (string, error) {
|
|||||||
}
|
}
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if _, err := DownloadLoTWConfirmations(ctx, nil, cfg, "2099-01-01", "", nil); err != nil {
|
if _, err := DownloadLoTWConfirmations(ctx, nil, cfg, "2099-01-01", "", false, nil); err != nil {
|
||||||
return "", fmt.Errorf("%s — but the DOWNLOAD login failed: %w", up, err)
|
return "", fmt.Errorf("%s — but the DOWNLOAD login failed: %w", up, err)
|
||||||
}
|
}
|
||||||
return up + ". Download login accepted.", nil
|
return up + ". Download login accepted.", nil
|
||||||
|
|||||||
Reference in New Issue
Block a user