Python lib/script to export Foreflight logbook
at main 2.7 kB view raw
1from typing import Any, cast 2import requests 3from fflogex.cli import main 4 5 6class ForeflightLogbookExporter: 7 _session: requests.Session 8 9 def __init__(self): 10 self._session = requests.Session() 11 12 def login(self, username: str, password: str): 13 """Login to Foreflight. 14 15 :param str username: Foreflight username 16 :param str password: Foreflight password 17 """ 18 19 self._session.cookies.clear() 20 resp = self._session.get("https://plan.foreflight.com/") 21 assert resp.ok 22 resp = self._session.post( 23 "https://plan.foreflight.com/auth/api/login", 24 json={"username": username, "password": password, "sessionType": "PRIVATE"}, 25 headers={"X-Xsrftoken": self._session.cookies.get("_xsrf")}, 26 ) 27 assert resp.ok 28 29 def request_export(self) -> str: 30 """Request Logbook Export on Foreflight. 31 32 Foreflight will start the asynchronous logbook export process and return a request ID. We will 33 use the ID to poll the export progress and subsequently get the link to the CSV file. 34 35 :return: logbook export request ID 36 :rtype: str 37 :raises AssertionError: if we failed to get the requestId 38 """ 39 40 out: Any = self._session.get( # pyright: ignore[reportAny,reportExplicitAny] 41 "https://plan.foreflight.com/logbook/api/export/csv" 42 ).json() 43 assert out["result"]["requestId"] is not None 44 requestId = cast(str, out["result"]["requestId"]) 45 46 return requestId 47 48 def get_link(self, requestId: str) -> str | None: 49 """Poll for download link. 50 51 Use the request ID returned from `request_export` to poll for the export progress and download 52 link. 53 54 :return: link to the CSV if completed, None if still in process. 55 :rtype: str | None 56 :raises AssertionError: if we failed to get the link 57 """ 58 59 out: Any = self._session.get( # pyright: ignore[reportAny,reportExplicitAny] 60 f"https://plan.foreflight.com/logbook/api/export/status/csv/{requestId}" 61 ).json() 62 if out["result"]["status"] == "FINISHED": 63 assert out["result"]["link"] is not None 64 return cast(str, out["result"]["link"]) 65 else: 66 return None 67 68 def download_link(self, link: str) -> str: 69 """Download the CSV file 70 71 Provided as a convenience method. You can also just use any tool to download the link returned 72 from the `get_link` method as it is usually a signed S3 URL. 73 74 :return: content of the logbook CSV export 75 :rtype: str 76 """ 77 78 return self._session.get(link).text 79 80 81def main_cli(): 82 main()