Contributing

Local setup

git clone https://github.com/ChunSikPark/TeamOverbyeWeather.git
cd TeamOverbyeWeather

pip install -e package/          # the SDK, editable
pip install -r docs/requirements.txt

Run the backend locally if you are changing it:

cd backend
pip install -r requirements.txt
uvicorn main:app --reload

Point the client at it:

client = WeatherClient(base_url="http://localhost:8000")

The backend needs GDRIVE_CREDENTIALS_JSON_CONTENT set to a service-account JSON with Viewer access on the data folders. Without it the catalog will be empty but the app still starts.

Building the docs

sphinx-build -b html docs docs/_build/html

Open docs/_build/html/index.html.

Pushing to main rebuilds and publishes the site to https://chunsikpark.github.io/TeamOverbyeWeather/ via .github/workflows/docs.yml. A .readthedocs.yaml is also checked in if you would rather host on Read the Docs, which adds versioned docs and PDF output.

Adding a data source

Five places, in this order:

  1. backend/catalog.py — add the folder id to _DEFAULT_FOLDERS, write a _build_<source>(), call it from build_catalog(), add the key to _empty_catalog()

  2. backend/download.py — add _SOURCE_LOOKUP and _FILENAME_PATTERNS entries

  3. package/TeamOverbyeWeather/registry.py — add to _API_KEYS and _TYPES

  4. frontend/main.js — add to TYPE_DEFS and getApiSourceKey()

  5. docs/guides/catalog.md — document the new type and its date-key format

Check the real filenames before writing a regex:

curl "<backend>/api/debug/folder?folder_id=<id>&limit=50"

Filename conventions vary between folders more than you would expect. Guessing wastes time; the debug endpoint takes seconds.

Things that will bite you

pww_io.py exists twice. backend/pww_io.py is the original; package/TeamOverbyeWeather/pww_io.py is a copy. Change one, copy to the other.

Bounding box ordering is (lat_max, lon_min, lat_min, lon_max) everywhere — north, west, south, east. Not the ordering most GIS libraries use.

The longitude axis descends. Index 0 is lon_max. Latitude ascends.

Always .copy() after slicing before writing a PWW. Numpy slices are views.

255 is the missing-data sentinel, not a value.

Times are OLE Automation days in the file, Unix epoch seconds in the API. crop_to_timerange takes epoch seconds; header["date_min"] is OLE days. Do not pass one where the other is expected — they are both floats, so nothing will stop you, and the result is a nonsensical date rather than an error.

HRRR history regexes are loose on the extension on purpose. Older archives are .pww.gz; do not tighten to \.zip$.

Do not reintroduce get_file_url in download.py. Fetching large Drive files by URL hits a virus-scan interstitial and silently returns HTML instead of data. Everything goes through the service account.

Testing a change to the crop path

Compare server output against local output — they should agree exactly:

from TeamOverbyeWeather import WeatherClient, pww_io, localcrop

client = WeatherClient(show_progress=False)
bbox = (36.5, -106.6, 25.8, -93.5)

server = client.download("hrrr", type="current", dates="2026-07-21",
                         region="TX", dest="./tmp")[0]
raw    = client._plain("hrrr_history_current", "2026-07-21",
                       __import__("pathlib").Path("./tmp"), False)
local  = localcrop.crop_file(raw, "./tmp/local.pww", bbox=bbox)

for p in (server, local):
    _, _, arr = pww_io.read_pww(open(p, "rb").read())
    print(p.name, arr.shape)

Both must report 96 time steps for a 15-minute day. A result of 24 means the four quarter files were not stitched.

Publishing

Package — bump version in package/pyproject.toml and __version__ in __init__.py (keep them equal), then build and upload:

cd package
rm -rf dist build
python -m build
python -m twine check dist/*
python -m twine upload dist/*

A version number on PyPI can never be reused, so run twine check first and confirm the metadata (especially [project.urls]) is right before uploading. Verify afterwards in a clean environment:

python -m venv /tmp/check && /tmp/check/bin/pip install TeamOverbyeWeather

Backend and frontend — push to main; both auto-deploy. Refresh the catalog afterwards:

curl <backend>/api/catalog/refresh

Style

Match the surrounding code. Google-style docstrings — they are what the API reference is generated from, so a function without one shows up bare in the docs.