Skip to main content

OxUtils

Production-ready utilities for Django applications in the Oxiliere ecosystem.

PyPI version Python 3.12+ Django 5.0+ Tests License Code style: ruff

Features

  • 🔐 JWT Authentication - RS256 with JWKS caching
  • 📝 Structured Logging - JSON logs with automatic request tracking
  • 🔍 Audit System - Change tracking with S3 export
  • ⚙️ Celery Integration - Pre-configured task processing
  • 🛠️ Django Mixins - UUID, timestamps, user tracking
  • Custom Exceptions - Standardized API errors
  • 🎨 Context Processors - Site name and domain for templates
  • 💱 Currency Module - Multi-source exchange rates (BCC/OXR)
  • 📄 PDF Generation - WeasyPrint integration for Django
  • 🏢 Multi-Tenant - PostgreSQL schema-based isolation
  • 🔑 Permissions — Domain-oriented RBAC with named actions, AND/OR logic, groups & grants, translatable labels

Installation

pip install oxutils
uv add oxutils

Quick Start

1. Configure Django Settings

# settings.py
from oxutils.conf import UTILS_APPS, AUDIT_MIDDLEWARE

INSTALLED_APPS = [
    *UTILS_APPS,  # structlog, auditlog, celery_results
    # your apps...
]

MIDDLEWARE = [
    *AUDIT_MIDDLEWARE,  # RequestMiddleware, Auditlog
    # your middleware...
]

2. Environment Variables

OXI_SERVICE_NAME=my-service
OXI_JWT_JWKS_URL=https://auth.example.com/.well-known/jwks.json

3. Usage Examples

# JWT Authentication
from oxutils.jwt.client import verify_token
payload = verify_token(token)

# Structured Logging
import structlog
logger = structlog.get_logger(__name__)
logger.info("user_action", user_id=user_id)


# Model Mixins
from oxutils.models.base import BaseModelMixin
class Product(BaseModelMixin):  # UUID + timestamps + is_active
    name = models.CharField(max_length=255)

# Custom Exceptions
from oxutils.exceptions import NotFoundException
raise NotFoundException(detail="User not found")

# Context Processors
# settings.py
TEMPLATES = [{
    'OPTIONS': {
        'context_processors': [
            'oxutils.context.site_name_processor.site_name',
        ],
    },
}]
# Now {{ site_name }} and {{ site_domain }} are available in templates

Documentation

Core Modules

Additional Modules

  • Currency - Exchange rates management
  • PDF - PDF generation with WeasyPrint
  • Oxiliere - Multi-tenant architecture
  • Permissions - RBAC with named actions

Requirements

  • Python 3.12+
  • Django 5.0+
  • PostgreSQL (recommended)

Development

git clone https://github.com/oxiliere/oxutils.git
cd oxutils
uv sync
uv run pytest  # 201 tests passing, 4 skipped

Creating Migrations

To generate Django migrations for the audit module:

make migrations
# or
uv run make_migrations.py

See MIGRATIONS.md for detailed documentation.

Optional Dependencies

# Multi-tenant support
uv add oxutils[oxiliere]

# PDF generation
uv add oxutils[pdf]

# Development tools
uv add oxutils[dev]

Advanced Examples

JWT with Django Ninja

from ninja import NinjaAPI
from ninja.security import HttpBearer
from oxutils.jwt.client import verify_token

class JWTAuth(HttpBearer):
    def authenticate(self, request, token):
        try:
            return verify_token(token)
        except:
            return None

api = NinjaAPI(auth=JWTAuth())

@api.get("/protected")
def protected(request):
    return {"user_id": request.auth['sub']}

Audit Log Export

from oxutils.audit.export import export_logs_from_date
from datetime import datetime, timedelta

from_date = datetime.now() - timedelta(days=7)
export = export_logs_from_date(from_date=from_date)
print(f"Exported to {export.data.url}")

Currency Exchange Rates

from oxutils.currency.models import CurrencyState

# Sync rates from BCC (with OXR fallback)
state = CurrencyState.sync()

# Get latest rates
latest = CurrencyState.objects.latest()
usd_rate = latest.currencies.get(code='USD').rate
eur_rate = latest.currencies.get(code='EUR').rate

PDF Generation

from oxutils.pdf.printer import Printer
from oxutils.pdf.views import WeasyTemplateView

# Standalone PDF generation
printer = Printer(
    template_name='invoice.html',
    context={'invoice': invoice},
    stylesheets=['css/invoice.css']
)
pdf_bytes = printer.write_pdf()

# Class-based view
class InvoicePDFView(WeasyTemplateView):
    template_name = 'invoice.html'
    pdf_filename = 'invoice.pdf'
    pdf_stylesheets = ['css/invoice.css']

Multi-Tenant Setup

# settings.py
TENANT_MODEL = "oxiliere.Tenant"
MIDDLEWARE = [
    'oxutils.oxiliere.middleware.TenantMainMiddleware',
    # ...
]

Permissions (v0.5.0)

# settings.py — define named actions & scopes with translatable labels
from django.utils.translation import gettext_lazy as _

PERMISSION_PRESET = {
    "actions": {
        "orders": {
            "create":  {"implies": [],          "label": _("Create")},
            "approve": {"implies": ["create"],  "label": _("Approve")},
            "cancel":  {"implies": [],          "label": _("Cancel")},
        },
        "articles": {
            "read":    {"implies": [],          "label": _("Read")},
            "write":   {"implies": ["read"],    "label": _("Write")},
            "publish": {"implies": ["write"],   "label": _("Publish")},
        },
    },
    "roles": [{"name": "Editor", "slug": "editor"}],
    "groups": [],
    "role_grants": [
        {"role": "editor", "scope": "articles", "actions": ["write"], "context": {}},
    ],
}

# Scopes — strings or dicts with labels (recommended for frontend i18n)
ACCESS_SCOPES = [
    "articles",
    {"key": "orders", "label": _("Orders")},
]

# Controller — AND (/) and OR (|) operators
from oxutils.permissions.perms import ScopePermission

@api_controller('/orders', permissions=[ScopePermission('orders:create/approve')])
class OrderController:  # user needs create AND approve
    ...

# Frontend endpoint — translated action labels
# GET /api/access/scopes/orders/actions
# → {"scope": "orders", "actions": [
#     {"key": "create",  "label": "Créer"},
#     {"key": "approve", "label": "Approuver"},
#   ]}

# Frontend endpoint — translated scope labels
# GET /api/access/scopes
# → [
#     {"key": "articles", "label": "Articles"},
#     {"key": "orders",   "label": "Commandes"},
#   ]

License

LGPL 3.0 License - see LICENSE

Support


Made with ❤️ by Oxiliere

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oxutils-0.5.0.tar.gz (124.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

oxutils-0.5.0-py3-none-any.whl (193.5 kB view details)

Uploaded Python 3

File details

Details for the file oxutils-0.5.0.tar.gz.

File metadata

  • Download URL: oxutils-0.5.0.tar.gz
  • Upload date:
  • Size: 124.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for oxutils-0.5.0.tar.gz
Algorithm Hash digest
SHA256 ec01085e55637c5211139f6bdd1b36078fba4199b8eab751b8a80057419c3db1
MD5 3c77aac4194bfc8d915eb3ef67ff2ec0
BLAKE2b-256 4e2fd064a44ad81760c0abe87c6188389115524b862d63c45a36946add532448

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxutils-0.5.0.tar.gz:

Publisher: publish.yml on oxiliere/oxutils

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oxutils-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: oxutils-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 193.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for oxutils-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c43e9fae70104e83a03edbe50aac5f6551a3fa89347dad52a8689467bc7d764d
MD5 5b6d585064b3c3be74bc6fa54eb2e6a4
BLAKE2b-256 4e7fc23d2becf04dfbac1d43dab973599b6856a0bdfba94ded542de631d92b40

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxutils-0.5.0-py3-none-any.whl:

Publisher: publish.yml on oxiliere/oxutils

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page