# grad300-client

Small Python package to access the GRAD300 backend API.

## Install

From PyPI (once published):

```bash
pip install grad300-client
```

From this repository:

```bash
pip install ./clients/grad300-client
```

## Build and Publish (uv)

This project uses `uv` workspace tooling, so you do not need to add `build` or `twine` as package dev dependencies.

Build artifacts for this package:

```bash
uv build --package grad300-client -o clients/grad300-client/dist
```

Publish to TestPyPI first:

```bash
export UV_PUBLISH_TOKEN="pypi-..."
uv publish \
    --publish-url https://test.pypi.org/legacy/ \
    --check-url https://test.pypi.org/simple/ \
    clients/grad300-client/dist/*
```

Publish to PyPI:

```bash
export UV_PUBLISH_TOKEN="pypi-..."
uv publish --check-url https://pypi.org/simple/ clients/grad300-client/dist/*
```

## Local Compatibility Tests (tox)

Run the client test suite across Python 3.9 to 3.13:

```bash
cd clients/grad300-client
python -m pip install tox
tox
```

If some interpreters are not installed locally, tox will skip them.

## GitLab CI Publishing (tags)

The repository CI can publish automatically to PyPI when a tag is pushed.

- Tag format: `v0.2` (also supports patch tags like `v0.2.1`).
- The package version is derived from Git tags via `hatch-vcs`, so there is no manual version update in `pyproject.toml` for releases.
- Configure `UV_PUBLISH_TOKEN` as a protected GitLab CI variable (PyPI token).

## Quick start

```python
from datetime import datetime
from grad300_client import Grad300

client = Grad300(user="student@example.com", password="your-password")

# Simple source query (astroquery-like helper)
tpi_scans = client.query_source("sun", scan_type="TPI")
print(tpi_scans["id", "scan_type", "file_name", "source", "date"])

# Query by scan filename fragment
named_scans = client.query_scan_name("2025062")

# Query by date range
march_scans = client.query_date_range(
    date_from=datetime(2026, 3, 1),
    date_to=datetime(2026, 3, 31, 23, 59, 59),
)

# Download one scan from a table row
if len(tpi_scans) > 0:
    # In-memory return (no destination_dir):
    # - TPI -> astropy.table.Table
    tpi_data = client.download(tpi_scans[0])

    # Save to file when destination_dir is provided
    local_path = client.download(tpi_scans[0], destination_dir="./downloads")
    print(local_path)

# Non-TPI in-memory return is an astropy.io.fits.HDUList
image_hdul = client.download_by_id(scan_type="Images", scan_id=30)
image_hdul.close()

client.close()
```

## Configuration (astropy.config)

`grad300-client` exposes a global `conf` object (Astropy-style) for defaults.

```python
from grad300_client import Grad300, conf

with (
    conf.set_temp("base_url", "https://staging.grad300.lam.fr"),
    conf.set_temp("api_prefix", "/api/v1"),
    conf.set_temp("timeout", 60.0),
    conf.set_temp("default_page_size", 200),
):
    client = Grad300(user="student@example.com", password="your-password")
    scans = client.query_source("sun", scan_type="TPI")
    client.close()
```

Constructor arguments still override config defaults.

## Notes

- The package wraps existing routes under `/api/v1`.
- `list`, `query_source`, `query_scan_name`, and `query_date_range` return `astropy.table.Table`.
- Use `list_records` and `query_records` if you prefer typed Python objects (`ScanRecord`).
- You can authenticate either with `token=...` or with `user=...` and `password=...` in the constructor.
- `download`, `download_by_id`, and `download_by_scan_name` fetch FITS data from scan download endpoints.
- If `destination_dir` is omitted: TPI returns an `astropy.table.Table`, other scan types return an `astropy.io.fits.HDUList`.
- If `destination_dir` is provided, the FITS file is saved and the returned value is the local `Path`.

## License

This package is distributed under the MIT License. See `LICENSE`.
