File size: 4,174 Bytes
942b115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""Kaggle PaySim dataset download script (SETUP-02).

Downloads the `ealaxi/paysim1` dataset from Kaggle into `data/` and
unzips it so the PaySim CSV lands directly under `data/`.

Note: the dataset originally lived at `ntnu-testimon/paysim1`; that slug now
returns HTTP 403 (dataset moved/retired under the old owner). `ealaxi/paysim1`
is the current live mirror of the same PaySim simulation output (same
~6.36M-row schema: step, type, amount, nameOrig/nameDest, balances, isFraud,
isFlaggedFraud).

Reads `KAGGLE_USERNAME`/`KAGGLE_KEY` from the environment (via `.env`,
loaded with python-dotenv) and fails loud -- naming the missing variable(s)
-- BEFORE attempting any network call or importing the `kaggle` package.

This check must run before `import kaggle`: the installed `kaggle` client
authenticates eagerly as a side effect of importing the package (any
submodule import triggers it), which would otherwise surface its own,
less specific error before our validation ever runs.

Idempotent: if a PaySim CSV already exists under `data/`, the download is
skipped and the existing path is reported.

Usage:
    python training/fetch_data.py
"""

from __future__ import annotations

import sys
from pathlib import Path

from dotenv import load_dotenv

DATASET = "ealaxi/paysim1"
DATA_DIR = Path(__file__).resolve().parent.parent / "data"


class FetchDataError(RuntimeError):
    """Raised when the PaySim dataset cannot be fetched.

    Messages never include credential values -- only variable names.
    """


def _require_kaggle_credentials() -> None:
    """Fail loud, naming any missing/empty Kaggle credential env var.

    Runs before any network call and before `kaggle` is imported.
    """
    import os

    missing = [
        name
        for name in ("KAGGLE_USERNAME", "KAGGLE_KEY")
        if not os.environ.get(name, "").strip()
    ]
    if missing:
        raise FetchDataError(
            "Missing required Kaggle credential(s) in the environment: "
            f"{', '.join(missing)}. Set them in .env (see README) before "
            "running training/fetch_data.py. No network call was made."
        )


def _existing_csv() -> Path | None:
    if not DATA_DIR.exists():
        return None
    csvs = sorted(DATA_DIR.glob("*.csv"))
    return csvs[0] if csvs else None


def main() -> Path:
    load_dotenv()
    _require_kaggle_credentials()

    existing = _existing_csv()
    if existing is not None:
        print(f"PaySim CSV already present, skipping download: {existing}")
        return existing

    # Imported lazily, after credential validation above: importing the
    # `kaggle` package (any submodule) triggers its own eager
    # authentication check as a side effect of import.
    from kaggle.api.kaggle_api_extended import KaggleApi
    from requests.exceptions import HTTPError

    DATA_DIR.mkdir(parents=True, exist_ok=True)

    try:
        api = KaggleApi()
        api.authenticate()
        print(f"Downloading {DATASET} into {DATA_DIR} ...")
        api.dataset_download_files(DATASET, path=str(DATA_DIR), unzip=True)
    except HTTPError as exc:
        status = exc.response.status_code if exc.response is not None else "unknown"
        raise FetchDataError(
            f"Kaggle API request failed (HTTP {status}) while downloading "
            f"'{DATASET}'. This is a Kaggle-side authorization/response "
            "issue, not a missing-credential problem -- KAGGLE_USERNAME/"
            "KAGGLE_KEY were present and accepted by the auth step. "
            "Verify the API token is current (regenerate at "
            "https://www.kaggle.com/settings/api if needed) and that the "
            "account has access to this dataset."
        ) from exc

    csv_path = _existing_csv()
    if csv_path is None:
        raise FetchDataError(
            f"Download completed but no .csv file was found under {DATA_DIR}. "
            "Check the Kaggle dataset contents."
        )
    print(f"PaySim CSV available at: {csv_path}")
    return csv_path


if __name__ == "__main__":
    try:
        main()
    except FetchDataError as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        sys.exit(1)