Metadata-Version: 2.4
Name: pycomad
Version: 0.1.0
Summary: Python client library for the Comad DAM (Digital Asset Management) API
License: MIT
Project-URL: Homepage, https://github.com/ullav-dev/pycomad
Project-URL: Documentation, https://ullav-dev.github.io/pycomad/
Project-URL: Bug Tracker, https://github.com/ullav-dev/pycomad/issues
Keywords: dam,digital asset management,comad,ullav
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-cov>=4; extra == "dev"
Requires-Dist: responses>=0.23; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: types-requests; extra == "dev"
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.25; extra == "docs"

# pycomad

Python client library for the [Comad DAM (Digital Asset Management)](https://github.com/colinmanning/ullav-dam-server) API.

## Installation

```bash
pip install pycomad
```

## Quick start

```python
from pycomad import ComadClient

client = ComadClient(
    api_url="http://localhost:8080",
    auth_url="http://localhost:8081",   # ullav-user-management service
)
client.login(email="user@example.com", password="secret")

# Upload a file (creates the record and uploads in one step)
asset = client.assets.upload("photo.jpg", creator="alice", is_private=False)

# Or create a record first, then upload the file
record = client.assets.create("Report", "application/pdf")
client.assets.upload_file(record.id, "report.pdf")

# Download and thumbnail
raw_bytes = client.assets.download(asset.id)
thumb_png  = client.assets.thumbnail(asset.id)

# Categories
cat = client.categories.create("Architecture", access_level="Global")
client.assets.add_category(asset.id, cat.id)
detail = client.assets.get(asset.id)   # AssetWithCategories

# Search
results = client.search.search(q="photo", creator="alice")
nearby  = client.search.nearby(lat=53.3, lon=-6.2, radius_km=5)

# Metadata (EXIF / IPTC / XMP)
meta = client.metadata.get(asset.id)
if meta.exif:
    print(meta.exif.get("camera_make"))

# Usage quotas
usage = client.assets.usage()
print(f"{usage.asset_count} assets, {usage.used_bytes // 1024**2} MB used")

# ZIP batch import
result = client.zip.upload("photos.zip", creator="alice")
print(f"Imported {len(result.assets)} assets into {len(result.categories)} categories")
```

## Example: upload a file

Print asset details after uploading a local file:

```python
import os
from pycomad import ComadClient

client = ComadClient(
    api_url=os.environ["COMAD_API_URL"],
    auth_url=os.environ.get("COMAD_AUTH_URL", os.environ["COMAD_API_URL"]),
)
client.login(email=os.environ["COMAD_EMAIL"], password=os.environ["COMAD_PASSWORD"])

asset = client.assets.upload("photo.jpg", creator="alice")
print(f"{asset.name}  ({asset.asset_type}, {asset.size:,} bytes)  id={asset.id}")
```

A runnable version of this script is at `examples/upload_asset.py`.

## Authentication

`ComadClient` authenticates against the `ullav-user-management` service, which
issues the JWT accepted by the Comad DAM server.

- `api_url` — Comad DAM server base URL (e.g. `http://comad-server:8080`)
- `auth_url` — auth service base URL (e.g. `http://ullav-user-management:8081`);
  omit if both services are behind the same proxy

Call `client.login(email, password)` before any other method. Tokens expire
according to the server's configuration; call `login()` again to refresh.

## Resource clients

| Attribute | Resource |
|---|---|
| `client.assets` | Asset CRUD, file upload/download/thumbnail, category links, usage |
| `client.metadata` | EXIF/IPTC/XMP metadata get and refresh |
| `client.search` | Full-text and metadata-driven search, geo search |
| `client.categories` | Category CRUD (hierarchical tree) |
| `client.custom_field_schemas` | Team custom field schema CRUD |
| `client.zip` | ZIP batch import |

## File uploads

`client.assets.upload()` and `client.assets.upload_file()` accept:

- A file path as a `str` or `pathlib.Path`
- An already-open binary file object (`IO[bytes]`)

The MIME type is inferred from the file extension if `asset_type` is omitted.

## Error handling

```python
from pycomad import ComadAuthError, ComadNotFoundError, ComadValidationError

try:
    asset = client.assets.get("non-existent-id")
except ComadNotFoundError:
    print("not found")
except ComadAuthError:
    client.login(email, password)   # token expired — re-authenticate
except ComadValidationError as e:
    print("bad request:", e)
```

| Exception | HTTP status |
|---|---|
| `ComadAuthError` | 401 / 403, or `login()` not called |
| `ComadNotFoundError` | 404 |
| `ComadValidationError` | 400 |
| `ComadServerError` | 5xx |
| `ComadError` | base class |

## Access levels

Use the `AccessLevel` enumeration or plain strings when creating categories:

```python
from pycomad import AccessLevel

client.categories.create("Press", access_level=AccessLevel.GLOBAL)
client.categories.create("Internal", access_level="Private")  # equivalent
```

## Licence

MIT
