@@ -186,6 +186,34 @@ def _int(key: str) -> int:
186186 end_date = kv .get ("END" , "" ),
187187 )
188188
189+ def _build_options (
190+ self ,
191+ band : str | None = None ,
192+ mode : str | None = None ,
193+ callsign : str | None = None ,
194+ dxcc : int | None = None ,
195+ start_date : str | None = None ,
196+ end_date : str | None = None ,
197+ confirmed_only : bool = False ,
198+ ) -> list [str ]:
199+ """Build OPTION filter list for FETCH requests."""
200+ options : list [str ] = []
201+ if band :
202+ options .append (f"BAND:{ band .upper ()} " )
203+ if mode :
204+ options .append (f"MODE:{ mode .upper ()} " )
205+ if callsign :
206+ options .append (f"CALL:{ callsign .upper ()} " )
207+ if dxcc is not None :
208+ options .append (f"DXCC:{ dxcc } " )
209+ if start_date :
210+ options .append (f"AFTER:{ start_date .replace ('-' , '' )} " )
211+ if end_date :
212+ options .append (f"BEFORE:{ end_date .replace ('-' , '' )} " )
213+ if confirmed_only :
214+ options .append ("STATUS:CONFIRMED" )
215+ return options
216+
189217 def fetch (
190218 self ,
191219 band : str | None = None ,
@@ -204,29 +232,12 @@ def fetch(
204232
205233 all_qsos : list [QsoRecord ] = []
206234 after_logid : str | None = None
235+ options = self ._build_options (band , mode , callsign , dxcc , start_date , end_date , confirmed_only )
207236
208237 while len (all_qsos ) < limit :
209238 params : dict [str , str ] = {"ACTION" : "FETCH" }
210-
211- # Build OPTION filter string
212- options : list [str ] = []
213- if band :
214- options .append (f"BAND:{ band .upper ()} " )
215- if mode :
216- options .append (f"MODE:{ mode .upper ()} " )
217- if callsign :
218- options .append (f"CALL:{ callsign .upper ()} " )
219- if dxcc is not None :
220- options .append (f"DXCC:{ dxcc } " )
221- if start_date :
222- options .append (f"AFTER:{ start_date .replace ('-' , '' )} " )
223- if end_date :
224- options .append (f"BEFORE:{ end_date .replace ('-' , '' )} " )
225- if confirmed_only :
226- options .append ("STATUS:CONFIRMED" )
227239 if options :
228240 params ["OPTION" ] = "," .join (options )
229-
230241 if after_logid :
231242 params ["AFTERLOGID" ] = after_logid
232243
@@ -257,3 +268,57 @@ def fetch(
257268 break
258269
259270 return all_qsos
271+
272+ def download_adif (
273+ self ,
274+ band : str | None = None ,
275+ mode : str | None = None ,
276+ start_date : str | None = None ,
277+ end_date : str | None = None ,
278+ ) -> dict [str , Any ]:
279+ """Download complete logbook as raw ADIF text.
280+
281+ Paginates through ALL records, concatenates ADIF fragments,
282+ and wraps with a proper ADIF header.
283+ """
284+ if _is_mock ():
285+ header = "<ADIF_VER:5>3.1.6\n <PROGRAMID:7>qrz-mcp\n <EOH>\n "
286+ adif_text = header + _MOCK_FETCH_ADIF
287+ record_count = adif_text .upper ().count ("<EOR>" )
288+ return {"adif" : adif_text , "record_count" : record_count }
289+
290+ fragments : list [str ] = []
291+ after_logid : str | None = None
292+ options = self ._build_options (band = band , mode = mode , start_date = start_date , end_date = end_date )
293+
294+ while True :
295+ params : dict [str , str ] = {"ACTION" : "FETCH" }
296+ if options :
297+ params ["OPTION" ] = "," .join (options )
298+ if after_logid :
299+ params ["AFTERLOGID" ] = after_logid
300+
301+ kv = self ._post (params )
302+
303+ adif = kv .get ("ADIF" , "" )
304+ if not adif :
305+ break
306+
307+ fragments .append (adif )
308+
309+ # Pagination cursor
310+ logids = kv .get ("LOGIDS" , "" )
311+ if logids :
312+ last_id = logids .split ("," )[- 1 ].strip ()
313+ if last_id and last_id != after_logid :
314+ after_logid = last_id
315+ else :
316+ break
317+ else :
318+ break
319+
320+ header = "<ADIF_VER:5>3.1.6\n <PROGRAMID:7>qrz-mcp\n <EOH>\n "
321+ body = "\n " .join (fragments )
322+ adif_text = header + body
323+ record_count = adif_text .upper ().count ("<EOR>" )
324+ return {"adif" : adif_text , "record_count" : record_count }
0 commit comments