Skip to content

Repository files navigation

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

About

Production-ready utilities for Django applications in the Oxiliere ecosystem

Resources

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages