Shared enums, email, geopoint
Project description
wr-common-lib
Shared enums, email helpers, geospatial coordinates, formatting utilities, and optional PostgreSQL write helpers.
| PyPI | wr-common-lib |
|---|---|
| import | wr_common_lib |
Requires Python 3.12+.
Install
pip install wr-common-lib
pip install "wr-common-lib[resend]" # ResendProvider (httpx)
pip install "wr-common-lib[db]" # EmailDbOper (async-db-tools)
pip install "wr-common-lib[db,resend]"
Package layout
src/wr_common_lib/
├── __init__.py # __version__
├── email/
│ ├── __init__.py
│ ├── constants.py # MailFlow, MailStatus, VoyageStatus, PassVia, CustomerRole
│ ├── encoding.py # file_to_base64, file_to_attachment
│ ├── provider.py # ResendProvider, SendResult, ReceivedEmail
│ └── db_oper.py # EmailDbOper, get_email_content_hash, …
├── geo/
│ ├── coordinates.py # DMS parse/format, longitude continuity
│ └── geo_point.py # GeoPoint
└── utils/
└── formatters.py # format_number, format_timestamp, format_timezone_offset
Content hash
from wr_common_lib.email import get_email_content_hash
content_hash = get_email_content_hash(
to="a@example.com,b@example.com",
subject="...",
content="...",
cc="",
attachments=[{"filename": "report.pdf"}],
)
Normalizes to / cc (comma-separated), strips subject/content, and hashes attachment filenames only (not file bytes).
Inbound parsing helpers
from wr_common_lib.email import extract_ship_candidates, extract_imo_from_content
extract_ship_candidates("MV OCEAN STAR - noon report")
extract_imo_from_content("IMO 9682930") # "9682930" or None
Attachment encoding
Converts local files to Base64 for Resend attachments[].content.
from wr_common_lib.email import file_to_base64, file_to_attachment
b64 = file_to_base64("/path/to/report.pdf")
att = file_to_attachment("/path/to/report.pdf")
# {"filename": "report.pdf", "content": "<base64>", "content_type": "application/pdf"}
ResendProvider
Requires httpx ([resend] extra). Uses the Resend API.
from wr_common_lib.email import EmailProvider, ResendProvider, file_to_attachment
provider = ResendProvider(
api_key="re_...",
default_from="noreply@example.com",
default_from_name="My App",
)
# Send (attachments from file_to_attachment)
result = await provider.send(
to="user@example.com",
cc="",
subject="Hello",
content="Plain text body",
attachments=[file_to_attachment("/path/to/file.pdf")],
mail_from=None, # optional override
email_id=email_id, # optional, sets X-Email-ID header
)
# result.ok, result.message_id
# Inbound (Receiving API)
mail = await provider.received(message_id)
# mail.ok, mail.subject, mail.text, mail.html, mail.from_addr, mail.to, mail.cc, ...
body = mail.content # text, else html
EmailDbOper
Requires the [db] extra. Pass an async-db-tools PostgresPool (or compatible pool with fetchval / execute).
from wr_common_lib.email import EmailDbOper, MailFlow, MailStatus, get_email_content_hash
db_oper = EmailDbOper(pool)
Write operations only; reads should use db_oper._db directly (e.g. fetchrow, fetchval).
insert_email
email_id, created = await db_oper.insert_email(
task,
MailFlow.OUTBOUND,
MailStatus.PENDING,
)
# created is True when a new row was inserted, False when content_hash already existed
Task fields (inbound and outbound use the same shape):
| Field | Required | Notes |
|---|---|---|
imo |
yes | |
voyage_id |
yes | UUID string |
mail_from |
yes | Sender |
to |
yes | Recipients (comma-separated allowed) |
cc |
no | Default "" |
subject |
no | Default "" |
content |
no | Default "" |
attachments |
no | List of dicts with filename |
content_hash |
no | Computed via get_email_content_hash when omitted |
created_user_id |
no | Outbound only |
On duplicate content_hash, the insert is skipped (ON CONFLICT DO NOTHING); the existing row id is returned and created is False.
update_status / update_parsed_data
await db_oper.update_status(email_id, MailStatus.SENT)
await db_oper.update_parsed_data(email_id, {"parsed": "..."})
Geo
Decimal degrees, DMS parsing/formatting, and anti-meridian track continuity. No extra dependencies.
from wr_common_lib.geo import (
GeoPoint,
parse_longitude,
parse_latitude,
longitude_to_dms,
make_longitude_continuous,
)
point = GeoPoint(lng=116.403874, lat=39.914883)
point = GeoPoint.from_string("116°24′14E", "39°54′53N")
point.lng_dms.formatted # e.g. 116°24′14E
point.to_dict() # {"lng": ..., "lat": ...}
track = make_longitude_continuous([{"lon": 170, "lat": 30}, {"lon": -170, "lat": 31}])
Utils
from wr_common_lib.utils import (
format_number,
format_timestamp,
format_timezone_offset,
)
format_number(3.1415, integer_digits=1, decimal_digits=2) # "3.14"
format_timezone_offset(8.5) # "+08:30"
format_timestamp(1710000000) # ISO UTC string
Dependencies
| install | brings in |
|---|---|
wr-common-lib |
enums, geo, utils, encoding, hash/parsing helpers, ResendProvider (needs httpx — use [resend]) |
wr-common-lib[db] |
async-db-tools, EmailDbOper, get_imo_and_voyage_id |
wr-common-lib[resend] |
httpx |
Your application owns API keys, database URL, and pool lifecycle.
Publish
pip install -e ".[db,resend]"
# 1. Update CHANGELOG.md for the new version
# 2. Bump version and publish
uv version 0.1.12
export UV_PUBLISH_TOKEN=pypi-...
uv build && uv publish
git tag v0.1.12 && git push --tags
PyPI does not allow re-uploading the same version.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wr_common_lib-0.1.11.tar.gz.
File metadata
- Download URL: wr_common_lib-0.1.11.tar.gz
- Upload date:
- Size: 12.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba1ca93ef57e287cdaaf3595fcb298b6492d295744b2658529732c431480780f
|
|
| MD5 |
1c36a65e0f49504fc5df0287dc3e0b48
|
|
| BLAKE2b-256 |
2728a9672cefddcf27d7919aaf32d4fe2a25393acae1bdb1ebced717d6d822b3
|
File details
Details for the file wr_common_lib-0.1.11-py3-none-any.whl.
File metadata
- Download URL: wr_common_lib-0.1.11-py3-none-any.whl
- Upload date:
- Size: 15.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0686f135bf3ff1db200a4960ab760f5bc28eaf513916041bcbb56d40b4b8bbe
|
|
| MD5 |
9b354037ddf9babec68408503fa67853
|
|
| BLAKE2b-256 |
d6663d0698f610e990d0110217fe539a23522fe82eb5e223467d0654956dde53
|