Metadata-Version: 2.4
Name: wr-common-lib
Version: 0.1.3
Summary: Shared enums and optional PostgreSQL helpers for email workflows
License: MIT
Requires-Python: >=3.12
Provides-Extra: db
Requires-Dist: async-db-tools>=0.1.0; extra == 'db'
Description-Content-Type: text/markdown

# wr-common-lib

Shared enums and optional PostgreSQL helpers for the `email` table.

| PyPI | `wr-common-lib` |
|------|-----------------|
| import | `wr_common_lib` |

Requires Python 3.12+.

## Install

```bash
pip install wr-common-lib
pip install "wr-common-lib[db]"   # adds async-db-tools
```

## Package layout

```
src/wr_common_lib/
├── __init__.py          # __version__
└── email/
    ├── __init__.py      # public exports
    ├── constants.py     # MailFlow, MailStatus
    ├── types.py         # OutboundTask, InboundTask
    └── db_oper.py       # EmailDbOper ([db] extra)
```

## Enums

`MailFlow` and `MailStatus` are `StrEnum` values aligned with PostgreSQL `mail_flow` / `mail_status`.

```python
from wr_common_lib.email import MailFlow, MailStatus

MailFlow.OUTBOUND
MailStatus.QUEUED.value   # use .value in raw SQL parameters
MailStatus.SENT.is_terminal
```

### Status flows

```
Outbound (sender):   PENDING → QUEUED → SENT | FAILED
Outbound (webhook):  SENT → DELIVERED | BOUNCED | DEFERRED
Inbound:             RECEIVED → PARSED | PARSE_FAILED
```

## EmailDbOper

Requires the `[db]` extra and a [`async-db-tools`](https://pypi.org/project/async-db-tools/) `PostgresPool`.

`EmailDbOper` is lazy-imported: `from wr_common_lib.email import MailStatus` does not require `async-db-tools`.

```python
from wr_common_lib.email import EmailDbOper, MailStatus, OutboundTask

db_oper = EmailDbOper(pool)

# Outbound
email_id = await db_oper.mark_queued(task)
await db_oper.mark_sent(email_id)
await db_oper.mark_failed(email_id)

# Lookup
row = await db_oper.find_by_content_hash("...")

# Inbound
email_id = await db_oper.insert_inbound_received(task, content_hash)
await db_oper.mark_parsed(email_id, {"key": "value"})
await db_oper.mark_parse_failed(email_id)
```

### Task dict fields

**Outbound** (`mark_queued`):

| Field | Required |
|-------|----------|
| `imo`, `voyage_id`, `mail_from`, `to`, `content_hash` | yes |
| `id` | no (update existing row) |
| `cc`, `subject`, `content`, `attachments`, `created_user_id` | no |

**Inbound** (`insert_inbound_received`):

| Field | Required |
|-------|----------|
| `imo`, `voyage_id`, `mail_from`, `mail_to` | yes |
| `mail_cc`, `subject`, `body_markdown`, `raw_text`, `attachments` | no |
| `brevo_uuid`, `message_id`, `in_reply_to` | no (stored in `parsed_data`) |

Inserts use `ON CONFLICT (content_hash)` for idempotent upserts.

## Dependencies

| install | brings in |
|---------|-----------|
| `wr-common-lib` | — |
| `wr-common-lib[db]` | `async-db-tools` |

Your application still owns the database URL and pool lifecycle.

## Development

```bash
pip install -e ".[db]"

uv version 0.1.2          # bump before release
export UV_PUBLISH_TOKEN=pypi-...
uv build && uv publish
```

PyPI does not allow re-uploading the same version; always bump the version for a new release.

## License

MIT — see [LICENSE](LICENSE).
