Metadata-Version: 2.4
Name: awaredb
Version: 1.0.0
Summary: Python client for AwareDB — a data-modeling and calculation platform with unit-aware formulas, scenarios, optimization, and reversible history.
Author-email: AwareDB Team <support@awaredb.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/djinalab/awaredb-python
Project-URL: Bug Tracker, https://github.com/djinalab/awaredb-python/issues
Project-URL: Documentation, https://dev.djina.com/
Project-URL: Source Code, https://github.com/djinalab/awaredb-python
Keywords: awaredb,calculations,formulas,units,scenarios,optimization,graph,api-client,modeling
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Database :: Front-Ends
Classifier: Topic :: Scientific/Engineering
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Typing :: Typed
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.28.1
Dynamic: license-file

# awaredb

Python client for [AwareDB](https://dev.djina.com/) — a data-modeling and calculation
platform with unit-aware formulas, reversible history, and a rich analyses toolbox
(scenarios, optimisation, sensitivity, projections, traces).

```bash
pip install awaredb
```

```python
from awaredb import AwareDBClient

with AwareDBClient(db="my_db", token="…") as client:
    client.calculate(formula="car.power * 2")
```

---

## Features

- Connect by token or by username/password.
- Read commands: `get`, `query`, `calculate`.
- Write commands: `update`, `remove`, `flush`, `load` (bulk-load JSON from disk).
- Analyses: `scenarios`, `impact`, `projection`, `goal_seek`, `optimize`,
  `sensitivity`, `trace`.
- History: `history` against the reversible audit database.
- Typed exceptions: `AwareDBError`, `TransportError`, `AuthError`,
  `InvalidCommandError`, `UnexpectedError`.
- Persistent `httpx`-backed transport with built-in retries and connection pooling.
- Ships a `py.typed` marker — works with `mypy` / `pyright` out of the box.

---

## Requirements

- Python 3.12+
- `httpx`

---

## Quick start

```python
from awaredb import AwareDBClient

# Token auth
client = AwareDBClient(db="my_db", token="…")

# Username / password — fetches a token at construction time
client = AwareDBClient(db="my_db", user="alice", password="…")

# Custom host + timeout, no upfront connection check
client = AwareDBClient(
    db="my_db",
    token="…",
    host="https://aware.example.com",
    timeout=30,
    check_connection=False,
)

# Close the underlying HTTP client when finished
client.close()

# Or use the client as a context manager
with AwareDBClient(db="my_db", token="…") as client:
    client.flush()
```

---

## Read commands

### `get(path, states=None)`

Return the value at a path.

```python
client.get(path="car.power")
# "250 hp"
```

### `query(nodes=None, conditions=None, properties=None, states=None, show_abstract=False)`

Return nodes matching the filters. `nodes` defaults to `["*"]` (all).

```python
client.query(
    nodes=["employee"],
    conditions=["node.salary.gross > 60000"],
    properties=["salary"],
)
```

### `calculate(formula, states=None)`

Evaluate one or more formulas against the live database.

```python
client.calculate(formula="car.power * 2")
# "500 hp"
```

---

## Write commands

### `update(data, partial=False)`

Create or update nodes / relations / relation types. `data` accepts a single item
or a list.

```python
client.update([
    {"uid": "fan", "power": "=sum(this.children.power)"},
])
```

### `remove(ids)`

Delete by id.

```python
client.remove(ids=["7037a8a5-…", "7037a8a5-…"])
```

### `flush()`

Drop every node and relation in the database.

### `load(filepath, recursive=False, flush=False)`

Push the contents of a JSON file (or folder of JSON files) through `update`.

```python
client.load("./seed", recursive=True, flush=True)
```

---

## Analyses

All analyses are read-only — they recalculate the graph against a temporary
context and return per-variant results. None mutate the database.

### `scenarios(edits, states=None)`

Apply path/value overrides, recalculate, return per-scenario results.

```python
client.scenarios(edits=[{"path": "motor.power", "value": "30 kW"}])
```

### `impact(edit, states=None, **extra)`

Sweep an input across N steps and observe outputs.

### `projection(start_from, unit="day", step=1, points=10, edits=None, states=None)`

Walk forward in time. Per-step `edits` apply between points.

### `goal_seek(target, decision, states=None, **extra)`

Find the input value that drives `target` to a desired value.

### `optimize(objectives, decisions, constraints=None, states=None, **extra)`

Vary decision variables to minimise / maximise objectives subject to constraints.

### `sensitivity(target, inputs, states=None, **extra)`

Perturb each input by ±delta and report the response per variant.

### `trace(target, depth=5, states=None)`

Walk the upstream dependency tree of a target path.

```python
client.trace(target="car.range", depth=10)
```

---

## History

### `history(change_id=None, ids=None, start=None, end=None, from_date=None, to_date=None)`

List changes recorded in the reversible history database.

```python
from datetime import datetime

client.history(from_date=datetime(2026, 1, 1))
```

---

## Errors

Everything funnels through `AwareDBError`. Subclasses indicate the failure mode:

| Exception              | When it fires                                            |
|------------------------|----------------------------------------------------------|
| `AuthError`            | 401 / 403, or username/password rejected.                |
| `InvalidCommandError`  | 400 — bad payload or unknown command.                    |
| `TransportError`       | Network failure or other non-2xx HTTP status.            |
| `UnexpectedError`      | 2xx body that isn't JSON.                                |
| `AwareDBError`         | Base class. Catch this to handle everything above.       |

---

## Development

[`uv`](https://github.com/astral-sh/uv) drives dependency management. The
lockfile (`uv.lock`) is committed and pins exact versions.

```bash
# One-time: install uv
brew install uv                            # macOS / Linuxbrew
# or:
curl -LsSf https://astral.sh/uv/install.sh | sh

# Provision the project env (creates .venv from uv.lock — runtime + dev group)
uv sync

# Runtime-only (excludes dev group: pytest, ruff, mypy, …)
uv sync --no-dev

# Tests, lint, format, type check
uv run pytest
uv run ruff check awaredb/ tests/
uv run ruff format --check awaredb/ tests/
uv run mypy awaredb/

# Add / remove deps (auto-updates pyproject.toml + uv.lock)
uv add some-package
uv add --group dev some-package
uv remove some-package
```

> **Note:** `python -m build` is the pip-era invocation and requires the `build`
> package installed in the env. With `uv`, use `uv build` instead — it uses
> `uv`'s own PEP 517 frontend, no extra dependency needed.

---

## Releasing

Releases are cut by pushing a `v*` git tag. The `.github/workflows/release.yml`
workflow builds the sdist + wheel with `uv build` and uploads them to PyPI via
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (OIDC, no token
in CI).

### One-time PyPI setup

1. Create the project on PyPI: <https://pypi.org/manage/account/publishing/>.
2. Add a **pending trusted publisher** with:
   - PyPI Project Name: `awaredb`
   - Owner: `djinalab`
   - Repository name: `awaredb-python`
   - Workflow name: `release.yml`
   - Environment name: `pypi`
3. In GitHub repo settings → Environments, create an environment named `pypi`
   (optionally with required reviewers for an approval gate).

### Cutting a release

```bash
# 1. Bump version in awaredb/__init__.py (e.g. 0.1.0 → 0.1.1)
# 2. Move the [Unreleased] block in CHANGELOG.md under a new [X.Y.Z] - YYYY-MM-DD heading
# 3. Commit, push to main
git commit -am "Release 0.1.1"
git push

# 4. Tag and push — release.yml fires automatically
git tag v0.1.1
git push origin v0.1.1
```

### Manual publish (fallback)

If you need to publish from a workstation:

```bash
uv build                                   # writes sdist + wheel to dist/
uv publish                                 # uses UV_PUBLISH_TOKEN env var
# or:
uv publish --token "$PYPI_API_TOKEN"
```

The `dist/` folder is gitignored — never commit build artifacts.

See [CONTRIBUTING.md](./CONTRIBUTING.md) and [CHANGELOG.md](./CHANGELOG.md).
