← back to ColonelPanicX__garmin-extract

Function bodies 373 total

All specs Real LLM only Function bodies
_render_day_list method · python · L598-L613 (16 LOC)
garmin_extract/gui/screens/pull_progress.py
    def _render_day_list(self) -> str:
        lines: list[str] = []
        for ds in self._day_states:
            if ds.status == "done":
                tally = min(ds.done + ds.failed, ds.total) if ds.total else ds.done + ds.failed
                count = f"({tally}/{ds.total})" if ds.total else ""
                failed = f"  {ds.failed} failed" if ds.failed else ""
                lines.append(f"\u2713 {ds.date_str}  {count}{failed}")
            elif ds.status == "active":
                count = f"{ds.done + ds.failed}/{ds.total}" if ds.total else "\u2026"
                lines.append(f"\u25cf {ds.date_str}  ({count})")
            elif ds.status == "skipped":
                lines.append(f"\u21b7 {ds.date_str}  (skipped)")
            else:
                lines.append(f"\u25cb  {ds.date_str}")
        return "\n".join(lines)
_render_metric_list method · python · L615-L622 (8 LOC)
garmin_extract/gui/screens/pull_progress.py
    def _render_metric_list(self) -> str:
        if not self._live_metrics:
            return "Waiting for first metric..."
        lines: list[str] = []
        for name, state in self._live_metrics:
            icon = "\u2713" if state == "done" else "\u2717"
            lines.append(f"{icon} {name}")
        return "\n".join(lines)
_cancel method · python · L626-L632 (7 LOC)
garmin_extract/gui/screens/pull_progress.py
    def _cancel(self) -> None:
        if self._proc is not None:
            try:
                self._proc.terminate()
            except Exception:
                pass
        self.reject()
closeEvent method · python · L634-L640 (7 LOC)
garmin_extract/gui/screens/pull_progress.py
    def closeEvent(self, event: object) -> None:  # noqa: N802
        if self._proc is not None:
            try:
                self._proc.terminate()
            except Exception:
                pass
        super().closeEvent(event)
_check_python function · python · L32-L34 (3 LOC)
garmin_extract/gui/screens/setup.py
def _check_python() -> tuple[bool, str]:
    v = sys.version_info
    return v >= (3, 12), f"Python {v.major}.{v.minor}.{v.micro}"
_check_chrome function · python · L37-L41 (5 LOC)
garmin_extract/gui/screens/setup.py
def _check_chrome() -> tuple[bool, str]:
    from garmin_extract.menu import _find_chrome

    found, version = _find_chrome()
    return found, version or "Not found"
_check_packages function · python · L44-L50 (7 LOC)
garmin_extract/gui/screens/setup.py
def _check_packages() -> tuple[bool, str]:
    from garmin_extract.menu import _missing_packages

    missing = _missing_packages()
    if not missing:
        return True, "All installed"
    return False, f"Missing: {', '.join(missing)}"
Open data scored by Repobility · https://repobility.com
_check_credentials function · python · L53-L56 (4 LOC)
garmin_extract/gui/screens/setup.py
def _check_credentials() -> tuple[bool, str]:
    from garmin_extract._credentials import check_credentials

    return check_credentials()
_check_gmail function · python · L59-L64 (6 LOC)
garmin_extract/gui/screens/setup.py
def _check_gmail() -> tuple[bool, str]:
    if not GMAIL_CREDS_FILE.exists():
        return False, "google_credentials.json missing"
    if not GMAIL_TOKEN_FILE.exists():
        return False, "Credentials found — not yet authorized"
    return True, "Authorized"
_StatusSignals class · python · L70-L76 (7 LOC)
garmin_extract/gui/screens/setup.py
class _StatusSignals(QObject):
    """Signals emitted from background threads to update the UI."""

    prereq_done = Signal(bool, str)  # (all_ok, summary)
    creds_done = Signal(bool, str)  # (ok, detail)
    gmail_done = Signal(bool, str)  # (ok, detail)
    all_done = Signal()
_StatusCard class · python · L82-L149 (68 LOC)
garmin_extract/gui/screens/setup.py
class _StatusCard(QFrame):
    """A clickable card showing a section name, status icon, and detail text."""

    def __init__(self, title: str, subtitle: str, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self.setObjectName("status-card")
        self.setCursor(Qt.CursorShape.PointingHandCursor)
        self.setStyleSheet("""
            QFrame#status-card {
                background-color: #313244;
                border: 1px solid #45475a;
                border-radius: 8px;
                padding: 16px;
            }
            QFrame#status-card:hover {
                border-color: #89b4fa;
            }
            """)

        layout = QHBoxLayout(self)
        layout.setContentsMargins(16, 12, 16, 12)

        # Left: icon + text
        left = QVBoxLayout()
        left.setSpacing(4)

        self._title = QLabel(title)
        self._title.setStyleSheet("font-size: 16px; font-weight: bold; background: transparent;")
        left.addWidget
__init__ method · python · L85-L129 (45 LOC)
garmin_extract/gui/screens/setup.py
    def __init__(self, title: str, subtitle: str, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self.setObjectName("status-card")
        self.setCursor(Qt.CursorShape.PointingHandCursor)
        self.setStyleSheet("""
            QFrame#status-card {
                background-color: #313244;
                border: 1px solid #45475a;
                border-radius: 8px;
                padding: 16px;
            }
            QFrame#status-card:hover {
                border-color: #89b4fa;
            }
            """)

        layout = QHBoxLayout(self)
        layout.setContentsMargins(16, 12, 16, 12)

        # Left: icon + text
        left = QVBoxLayout()
        left.setSpacing(4)

        self._title = QLabel(title)
        self._title.setStyleSheet("font-size: 16px; font-weight: bold; background: transparent;")
        left.addWidget(self._title)

        self._subtitle = QLabel(subtitle)
        self._subtitle.setStyleSheet("font-size: 13p
set_status method · python · L131-L142 (12 LOC)
garmin_extract/gui/screens/setup.py
    def set_status(self, ok: bool, detail: str) -> None:
        if ok:
            self._icon.setText("✓")
            self._icon.setStyleSheet("font-size: 22px; color: #a6e3a1; background: transparent;")
        else:
            self._icon.setText("✗")
            self._icon.setStyleSheet("font-size: 22px; color: #f38ba8; background: transparent;")
        # Strip Rich markup for GUI display
        import re

        clean = re.sub(r"\[/?[^\]]*\]", "", detail)
        self._detail.setText(clean)
mousePressEvent method · python · L147-L149 (3 LOC)
garmin_extract/gui/screens/setup.py
    def mousePressEvent(self, event: object) -> None:  # noqa: N802
        if self._callback and callable(self._callback):
            self._callback()
PrereqDialog class · python · L155-L225 (71 LOC)
garmin_extract/gui/screens/setup.py
class PrereqDialog(QDialog):
    """Modal dialog that runs prerequisite checks with live output."""

    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self.setWindowTitle("Prerequisites")
        self.setMinimumSize(600, 400)
        self.resize(700, 500)

        layout = QVBoxLayout(self)

        self._log = QTextEdit()
        self._log.setReadOnly(True)
        self._log.setStyleSheet(
            "background-color: #1e1e2e; color: #cdd6f4; font-family: monospace;"
            " font-size: 13px; border: 1px solid #45475a; border-radius: 4px;"
        )
        layout.addWidget(self._log)

        self._progress = QProgressBar()
        self._progress.setMaximum(3)
        self._progress.setValue(0)
        layout.addWidget(self._progress)

        self._status = QLabel("Running checks…")
        self._status.setStyleSheet("color: #6c7086;")
        self._status.setAlignment(Qt.AlignmentFlag.AlignCenter)
        layout.addWidget
Repobility (the analyzer behind this table) · https://repobility.com
__init__ method · python · L158-L197 (40 LOC)
garmin_extract/gui/screens/setup.py
    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self.setWindowTitle("Prerequisites")
        self.setMinimumSize(600, 400)
        self.resize(700, 500)

        layout = QVBoxLayout(self)

        self._log = QTextEdit()
        self._log.setReadOnly(True)
        self._log.setStyleSheet(
            "background-color: #1e1e2e; color: #cdd6f4; font-family: monospace;"
            " font-size: 13px; border: 1px solid #45475a; border-radius: 4px;"
        )
        layout.addWidget(self._log)

        self._progress = QProgressBar()
        self._progress.setMaximum(3)
        self._progress.setValue(0)
        layout.addWidget(self._progress)

        self._status = QLabel("Running checks…")
        self._status.setStyleSheet("color: #6c7086;")
        self._status.setAlignment(Qt.AlignmentFlag.AlignCenter)
        layout.addWidget(self._status)

        btn_layout = QHBoxLayout()
        btn_layout.addStretch()
        self._clos
_run_checks method · python · L199-L217 (19 LOC)
garmin_extract/gui/screens/setup.py
    def _run_checks(self) -> None:
        checks = [
            ("Python 3.12+", _check_python),
            ("Google Chrome", _check_chrome),
            ("Python packages", _check_packages),
        ]
        failed: list[str] = []
        for i, (name, fn) in enumerate(checks):
            ok, detail = fn()
            icon = "✓" if ok else "✗"
            self._signals.log_line.emit(f"  {icon}  {name}: {detail}")
            if not ok:
                failed.append(name)
            self._signals.progress.emit(i + 1)

        if failed:
            self._signals.finished.emit(f"Issues found: {', '.join(failed)}")
        else:
            self._signals.finished.emit("All checks passed ✓")
_on_finished method · python · L222-L225 (4 LOC)
garmin_extract/gui/screens/setup.py
    def _on_finished(self, summary: str) -> None:
        self._status.setText(summary)
        self._close_btn.setEnabled(True)
        self._progress.hide()
_CheckSignals class · python · L228-L231 (4 LOC)
garmin_extract/gui/screens/setup.py
class _CheckSignals(QObject):
    log_line = Signal(str)
    progress = Signal(int)
    finished = Signal(str)
CredentialsDialog class · python · L237-L388 (152 LOC)
garmin_extract/gui/screens/setup.py
class CredentialsDialog(QDialog):
    """Modal dialog for entering Garmin Connect credentials."""

    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self.setWindowTitle("Garmin Connect Credentials")
        self.setMinimumWidth(480)
        self.setModal(True)

        layout = QVBoxLayout(self)
        layout.setSpacing(12)

        # Keyring status
        self._mode_label = QLabel("Detecting keyring…")
        self._mode_label.setStyleSheet("color: #6c7086; font-size: 13px;")
        layout.addWidget(self._mode_label)

        # Warning panel (hidden by default)
        self._warning = QLabel()
        self._warning.setWordWrap(True)
        self._warning.setStyleSheet(
            "background-color: #45475a; color: #f38ba8; border: 1px solid #f38ba8;"
            " border-radius: 6px; padding: 12px; font-size: 13px;"
        )
        self._warning.hide()
        layout.addWidget(self._warning)

        # Email
        layout.ad
__init__ method · python · L240-L301 (62 LOC)
garmin_extract/gui/screens/setup.py
    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self.setWindowTitle("Garmin Connect Credentials")
        self.setMinimumWidth(480)
        self.setModal(True)

        layout = QVBoxLayout(self)
        layout.setSpacing(12)

        # Keyring status
        self._mode_label = QLabel("Detecting keyring…")
        self._mode_label.setStyleSheet("color: #6c7086; font-size: 13px;")
        layout.addWidget(self._mode_label)

        # Warning panel (hidden by default)
        self._warning = QLabel()
        self._warning.setWordWrap(True)
        self._warning.setStyleSheet(
            "background-color: #45475a; color: #f38ba8; border: 1px solid #f38ba8;"
            " border-radius: 6px; padding: 12px; font-size: 13px;"
        )
        self._warning.hide()
        layout.addWidget(self._warning)

        # Email
        layout.addWidget(QLabel("Email"))
        self._email = QLineEdit()
        self._email.setPlaceholderText("
_load_existing method · python · L303-L314 (12 LOC)
garmin_extract/gui/screens/setup.py
    def _load_existing(self) -> None:
        try:
            from garmin_extract._credentials import load_credentials

            email, _ = load_credentials()
            if email:
                self._email.setText(email)
                self._password.setFocus()
            else:
                self._email.setFocus()
        except Exception:
            self._email.setFocus()
_detect_keyring method · python · L316-L323 (8 LOC)
garmin_extract/gui/screens/setup.py
    def _detect_keyring(self) -> None:
        from garmin_extract._credentials import detect_keyring

        ok, detail = detect_keyring()
        # Use QMetaObject.invokeMethod for thread safety — but simpler
        # to use a signal. For brevity, just set and update in main thread.
        self._keyring_available = ok
        self._keyring_detail = detail
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
showEvent method · python · L325-L349 (25 LOC)
garmin_extract/gui/screens/setup.py
    def showEvent(self, event: object) -> None:  # noqa: N802
        super().showEvent(event)
        # Poll for keyring detection result (runs nearly instantly)
        from PySide6.QtCore import QTimer

        def _apply() -> None:
            if self._keyring_available is None:
                QTimer.singleShot(50, _apply)
                return
            if self._keyring_available:
                self._mode_label.setText(f"● Keyring: {self._keyring_detail}")
                self._mode_label.setStyleSheet("color: #a6e3a1; font-size: 13px;")
            else:
                self._mode_label.setText(
                    "⚠ No secure keyring — credentials will be saved to .env (plaintext)"
                )
                self._mode_label.setStyleSheet("color: #f9e2af; font-size: 13px;")
                self._warning.setText(
                    "⚠ PLAINTEXT WARNING\n\n"
                    "Saving will write your password to a plain-text file on disk.\n"
                    "An
_save method · python · L351-L388 (38 LOC)
garmin_extract/gui/screens/setup.py
    def _save(self) -> None:
        from garmin_extract._credentials import save_to_env, save_to_keyring

        email = self._email.text().strip()
        password = self._password.text().strip()

        self._error.hide()
        self._success.hide()

        if not email:
            self._error.setText("Email is required.")
            self._error.show()
            self._email.setFocus()
            return
        if not password:
            self._error.setText("Password is required.")
            self._error.show()
            self._password.setFocus()
            return

        if self._keyring_available:
            ok, detail = save_to_keyring(email, password)
            if ok:
                self._success.setText(f"✓ Saved to keyring — {email}")
                self._success.show()
            else:
                self._error.setText(f"Keyring save failed: {detail}")
                self._error.show()
                return
        else:
            save_to_env(email,
GmailOAuthDialog class · python · L394-L524 (131 LOC)
garmin_extract/gui/screens/setup.py
class GmailOAuthDialog(QDialog):
    """Modal dialog for Gmail OAuth authorization flow."""

    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self.setWindowTitle("Gmail OAuth Setup")
        self.setMinimumSize(650, 450)
        self.setModal(True)

        layout = QVBoxLayout(self)

        self._log = QTextEdit()
        self._log.setReadOnly(True)
        self._log.setStyleSheet(
            "background-color: #1e1e2e; color: #cdd6f4; font-family: monospace;"
            " font-size: 13px; border: 1px solid #45475a; border-radius: 4px;"
        )
        layout.addWidget(self._log)

        # Code input area (hidden until needed)
        self._code_frame = QFrame()
        self._code_frame.hide()
        code_layout = QVBoxLayout(self._code_frame)
        code_layout.setContentsMargins(0, 8, 0, 0)
        code_layout.addWidget(QLabel("Paste the authorization code below:"))
        self._code_input = QLineEdit()
        self._co
__init__ method · python · L397-L443 (47 LOC)
garmin_extract/gui/screens/setup.py
    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self.setWindowTitle("Gmail OAuth Setup")
        self.setMinimumSize(650, 450)
        self.setModal(True)

        layout = QVBoxLayout(self)

        self._log = QTextEdit()
        self._log.setReadOnly(True)
        self._log.setStyleSheet(
            "background-color: #1e1e2e; color: #cdd6f4; font-family: monospace;"
            " font-size: 13px; border: 1px solid #45475a; border-radius: 4px;"
        )
        layout.addWidget(self._log)

        # Code input area (hidden until needed)
        self._code_frame = QFrame()
        self._code_frame.hide()
        code_layout = QVBoxLayout(self._code_frame)
        code_layout.setContentsMargins(0, 8, 0, 0)
        code_layout.addWidget(QLabel("Paste the authorization code below:"))
        self._code_input = QLineEdit()
        self._code_input.setPlaceholderText("Paste code here")
        self._code_input.returnPressed.connect
_start_flow method · python · L445-L471 (27 LOC)
garmin_extract/gui/screens/setup.py
    def _start_flow(self) -> None:
        if not GMAIL_CREDS_FILE.exists():
            self._log.append(
                "google_credentials.json not found.\n\n"
                "To create it:\n"
                "  1. Go to https://console.cloud.google.com\n"
                "  2. Create a project\n"
                "  3. Enable the Gmail API\n"
                "  4. Credentials → Create → OAuth 2.0 Client ID → Desktop app\n"
                "  5. Download JSON → save as google_credentials.json\n"
                "     in the project root directory\n\n"
                "Then come back here to complete authorization."
            )
            self._status.setText("google_credentials.json required")
            return

        if GMAIL_TOKEN_FILE.exists():
            self._log.append(
                "✓  Gmail MFA is already authorized.\n\n"
                "Token file found: .google_token.json\n\n"
                "To re-authorize, delete .google_token.json and run this again."
    
_do_auth method · python · L473-L504 (32 LOC)
garmin_extract/gui/screens/setup.py
    def _do_auth(self) -> None:
        import subprocess

        try:
            proc = subprocess.Popen(
                [sys.executable, "-u", str(ROOT / "scripts" / "setup_gmail_auth.py")],
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                stdin=subprocess.DEVNULL,
                text=True,
                cwd=str(ROOT),
            )
            assert proc.stdout is not None
            code_shown = False
            for raw_line in iter(proc.stdout.readline, ""):
                line = raw_line.rstrip("\n")
                self._signals.log_line.emit(line)

                if line.startswith("https://") and not code_shown:
                    self._auth_url = line.strip()

                if "Waiting up to" in line and not code_shown:
                    code_shown = True
                    self._signals.show_code_input.emit(self._auth_url)

            proc.wait()
            if proc.returncode == 0:
                self._sign
_show_code_input method · python · L509-L512 (4 LOC)
garmin_extract/gui/screens/setup.py
    def _show_code_input(self, auth_url: str) -> None:
        self._status.setText("Open the URL above in any browser, then paste the code below")
        self._code_frame.show()
        self._code_input.setFocus()
_submit_code method · python · L514-L520 (7 LOC)
garmin_extract/gui/screens/setup.py
    def _submit_code(self) -> None:
        code = self._code_input.text().strip()
        if not code:
            return
        GMAIL_AUTH_CODE_FILE.write_text(code + "\n")
        self._code_frame.hide()
        self._status.setText("Code submitted — waiting for confirmation…")
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
_on_finished method · python · L522-L524 (3 LOC)
garmin_extract/gui/screens/setup.py
    def _on_finished(self, summary: str) -> None:
        self._status.setText(summary)
        self._code_frame.hide()
_GmailSignals class · python · L527-L530 (4 LOC)
garmin_extract/gui/screens/setup.py
class _GmailSignals(QObject):
    log_line = Signal(str)
    show_code_input = Signal(str)
    finished = Signal(str)
SetupPage class · python · L536-L617 (82 LOC)
garmin_extract/gui/screens/setup.py
class SetupPage(QWidget):
    """The Initial Setup page shown in the main window's stacked widget."""

    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)

        layout = QVBoxLayout(self)
        layout.setContentsMargins(40, 32, 40, 32)
        layout.setSpacing(8)

        heading = QLabel("Initial Setup")
        heading.setObjectName("heading")
        layout.addWidget(heading)

        sub = QLabel("Configure credentials and prerequisites")
        sub.setObjectName("subheading")
        layout.addWidget(sub)

        layout.addSpacing(16)

        # ── Prerequisite card ─────────────────────────
        self._prereq_card = _StatusCard("Prerequisites", "Python, Chrome, packages")
        self._prereq_card.on_click(self._open_prereqs)
        layout.addWidget(self._prereq_card)

        layout.addSpacing(4)

        # ── Credentials card ──────────────────────────
        self._creds_card = _StatusCard("Garmin Credentials", "Email and 
__init__ method · python · L539-L582 (44 LOC)
garmin_extract/gui/screens/setup.py
    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)

        layout = QVBoxLayout(self)
        layout.setContentsMargins(40, 32, 40, 32)
        layout.setSpacing(8)

        heading = QLabel("Initial Setup")
        heading.setObjectName("heading")
        layout.addWidget(heading)

        sub = QLabel("Configure credentials and prerequisites")
        sub.setObjectName("subheading")
        layout.addWidget(sub)

        layout.addSpacing(16)

        # ── Prerequisite card ─────────────────────────
        self._prereq_card = _StatusCard("Prerequisites", "Python, Chrome, packages")
        self._prereq_card.on_click(self._open_prereqs)
        layout.addWidget(self._prereq_card)

        layout.addSpacing(4)

        # ── Credentials card ──────────────────────────
        self._creds_card = _StatusCard("Garmin Credentials", "Email and password")
        self._creds_card.on_click(self._open_credentials)
        layout.addWidget(self._cre
_check_all method · python · L587-L602 (16 LOC)
garmin_extract/gui/screens/setup.py
    def _check_all(self) -> None:
        py_ok, _ = _check_python()
        chrome_ok, _ = _check_chrome()
        pkg_ok, _ = _check_packages()
        all_ok = py_ok and chrome_ok and pkg_ok

        parts = []
        for ok, name in [(py_ok, "Python"), (chrome_ok, "Chrome"), (pkg_ok, "Packages")]:
            parts.append(f"{'✓' if ok else '✗'} {name}")
        self._signals.prereq_done.emit(all_ok, "  ".join(parts))

        creds_ok, creds_str = _check_credentials()
        self._signals.creds_done.emit(creds_ok, creds_str)

        gmail_ok, gmail_str = _check_gmail()
        self._signals.gmail_done.emit(gmail_ok, gmail_str)
_open_prereqs method · python · L604-L607 (4 LOC)
garmin_extract/gui/screens/setup.py
    def _open_prereqs(self) -> None:
        dlg = PrereqDialog(self)
        dlg.exec()
        self.refresh_status()
_open_credentials method · python · L609-L612 (4 LOC)
garmin_extract/gui/screens/setup.py
    def _open_credentials(self) -> None:
        dlg = CredentialsDialog(self)
        dlg.exec()
        self.refresh_status()
_open_gmail method · python · L614-L617 (4 LOC)
garmin_extract/gui/screens/setup.py
    def _open_gmail(self) -> None:
        dlg = GmailOAuthDialog(self)
        dlg.exec()
        self.refresh_status()
Open data scored by Repobility · https://repobility.com
_run_script_if_frozen function · python · L20-L57 (38 LOC)
garmin_extract/__main__.py
def _run_script_if_frozen() -> None:
    """If invoked as `garmin-extract.exe -u <script> [args...]`, run the script."""
    if not getattr(sys, "frozen", False):
        return
    if len(sys.argv) < 3 or sys.argv[1] != "-u":
        return

    script = sys.argv[2]
    sys.argv = [script] + sys.argv[3:]

    # Add the script's directory to sys.path so sibling imports work
    # (e.g. pullers/garmin.py does `from _gmail_mfa import ...`)
    from pathlib import Path

    script_dir = str(Path(script).resolve().parent)
    if script_dir not in sys.path:
        sys.path.insert(0, script_dir)

    # Unbuffered UTF-8 output so the GUI sees progress in real time
    # (Windows default pipe encoding is cp1252 which can't encode ✓/✗ etc.)
    for stream in (sys.stdout, sys.stderr):
        try:
            stream.reconfigure(encoding="utf-8", line_buffering=True, write_through=True)
        except Exception:
            pass

    import runpy

    try:
        runpy.run_path(script, run_name
prompt_with_navigation function · python · L51-L61 (11 LOC)
garmin_extract/menu.py
def prompt_with_navigation(prompt_text: str) -> str:
    """Wrap any input prompt; raise navigation signals on b / x / q."""
    response = input(prompt_text).strip()
    lower = response.lower()
    if lower == "b":
        raise BackSignal
    if lower == "x":
        raise ExitToMainSignal
    if lower == "q":
        raise QuitSignal
    return response
_continue function · python · L64-L66 (3 LOC)
garmin_extract/menu.py
def _continue(prompt: str = "\n  Press Enter to continue...") -> None:
    """Pause with a continue prompt. Navigation signals propagate normally."""
    prompt_with_navigation(prompt)
header function · python · L78-L86 (9 LOC)
garmin_extract/menu.py
def header(title: str) -> None:
    content = f"  {title}  "
    width = max(50, len(content))
    padding = width - len(content)
    print()
    console.print(f"╔{'═' * width}╗")
    console.print(f"║[bold]{content}{' ' * padding}[/]║")
    console.print(f"╚{'═' * width}╝")
    print()
load_env function · python · L93-L101 (9 LOC)
garmin_extract/menu.py
def load_env() -> dict[str, str]:
    vals: dict[str, str] = {}
    if ENV.exists():
        for line in ENV.read_text().splitlines():
            line = line.strip()
            if line and not line.startswith("#") and "=" in line:
                k, _, v = line.partition("=")
                vals[k.strip()] = v.strip()
    return vals
save_env function · python · L104-L110 (7 LOC)
garmin_extract/menu.py
def save_env(vals: dict[str, str]) -> None:
    lines = [
        "# Garmin Connect credentials",
        f"GARMIN_EMAIL={vals.get('GARMIN_EMAIL', '')}",
        f"GARMIN_PASSWORD={vals.get('GARMIN_PASSWORD', '')}",
    ]
    ENV.write_text("\n".join(lines) + "\n")
_find_chrome function · python · L118-L163 (46 LOC)
garmin_extract/menu.py
def _find_chrome() -> tuple[bool, str | None]:
    system = platform.system()

    # Windows: chrome.exe --version launches Chrome instead of printing version.
    # Check standard install paths and read VersionInfo via PowerShell.
    if system == "Windows":
        paths = [
            r"C:\Program Files\Google\Chrome\Application\chrome.exe",
            r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
            os.path.expandvars(r"%LOCALAPPDATA%\Google\Chrome\Application\chrome.exe"),
        ]
        for path in paths:
            if not Path(path).is_file():
                continue
            try:
                ps = f"(Get-Item '{path}').VersionInfo.ProductVersion"
                r = subprocess.run(
                    ["powershell", "-NoProfile", "-Command", ps],
                    capture_output=True,
                    text=True,
                    timeout=5,
                )
                version = r.stdout.strip() if r.returncode == 0 else ""
  
_find_xvfb function · python · L166-L171 (6 LOC)
garmin_extract/menu.py
def _find_xvfb() -> bool:
    try:
        r = subprocess.run(["which", "Xvfb"], capture_output=True, timeout=5)
        return r.returncode == 0
    except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
        return False
Repobility (the analyzer behind this table) · https://repobility.com
_missing_packages function · python · L174-L188 (15 LOC)
garmin_extract/menu.py
def _missing_packages() -> list[str]:
    checks = [
        ("seleniumbase", "seleniumbase"),
        ("dotenv", "python-dotenv"),
        ("google.oauth2", "google-auth"),
        ("googleapiclient", "google-api-python-client"),
        ("requests_oauthlib", "requests-oauthlib"),
    ]
    missing = []
    for mod, pkg in checks:
        try:
            __import__(mod)
        except ImportError:
            missing.append(pkg)
    return missing
check_prerequisites function · python · L196-L485 (290 LOC)
garmin_extract/menu.py
def check_prerequisites() -> None:  # noqa: C901
    header("Setup Wizard")
    system = platform.system()
    issues: list[str] = []

    # ── Step 1: Python ───────────────────────────────────────────────────────
    print("Step 1 of 5 — Python version")
    print()
    v = sys.version_info
    ok = v >= (3, 12)

    if ok:
        console.print(f"  [green]✓[/]  Python {v.major}.{v.minor}.{v.micro}")
    else:
        console.print(
            f"  [red]✗[/]  Python {v.major}.{v.minor}.{v.micro}"
            " — version 3.12 or newer is required."
        )
        print()
        print("  Python is the programming language this tool is written in.")
        print("  Version 3.12 added features this code depends on.")
        print()
        if system == "Windows":
            print("  Download the latest Python from: https://www.python.org/downloads/")
            print()
            print("  During installation, make sure to check:")
            print('  ☑  "Add Python to PATH"')
 
configure_credentials function · python · L493-L524 (32 LOC)
garmin_extract/menu.py
def configure_credentials() -> None:
    header("Configure Garmin Credentials")
    print("  Stored in .env (gitignored — never committed).\n")
    env = load_env()

    cur_email = env.get("GARMIN_EMAIL", "")
    cur_pass = env.get("GARMIN_PASSWORD", "")

    if cur_email:
        print(f"  Current email:    {cur_email}")
        new_email = prompt_with_navigation("  New email         (Enter to keep): ")
        email = new_email or cur_email
    else:
        email = prompt_with_navigation("  Garmin email: ")

    if cur_pass:
        print(f"  Current password: {'*' * min(len(cur_pass), 12)}")
        new_pass = getpass.getpass("  New password      (Enter to keep): ")
        password = new_pass or cur_pass
    else:
        password = getpass.getpass("  Garmin password: ")

    if not email or not password:
        print("\n  No changes made.")
        _continue()
        return

    env["GARMIN_EMAIL"] = email
    env["GARMIN_PASSWORD"] = password
    save_env(env)
    console.pri
‹ prevpage 3 / 8next ›