oxutils 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oxutils-0.1.5/PKG-INFO +294 -0
- oxutils-0.1.5/README.md +254 -0
- oxutils-0.1.5/pyproject.toml +111 -0
- oxutils-0.1.5/src/oxutils/__init__.py +23 -0
- oxutils-0.1.5/src/oxutils/apps.py +14 -0
- oxutils-0.1.5/src/oxutils/audit/__init__.py +20 -0
- oxutils-0.1.5/src/oxutils/audit/apps.py +12 -0
- oxutils-0.1.5/src/oxutils/audit/export.py +229 -0
- oxutils-0.1.5/src/oxutils/audit/masks.py +97 -0
- oxutils-0.1.5/src/oxutils/audit/migrations/0001_initial.py +41 -0
- oxutils-0.1.5/src/oxutils/audit/migrations/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/audit/models.py +75 -0
- oxutils-0.1.5/src/oxutils/audit/settings.py +4 -0
- oxutils-0.1.5/src/oxutils/audit/utils.py +22 -0
- oxutils-0.1.5/src/oxutils/celery/__init__.py +1 -0
- oxutils-0.1.5/src/oxutils/celery/base.py +98 -0
- oxutils-0.1.5/src/oxutils/celery/settings.py +1 -0
- oxutils-0.1.5/src/oxutils/conf.py +11 -0
- oxutils-0.1.5/src/oxutils/constants.py +2 -0
- oxutils-0.1.5/src/oxutils/context/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/context/site_name_processor.py +11 -0
- oxutils-0.1.5/src/oxutils/currency/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/currency/admin.py +57 -0
- oxutils-0.1.5/src/oxutils/currency/apps.py +7 -0
- oxutils-0.1.5/src/oxutils/currency/controllers.py +79 -0
- oxutils-0.1.5/src/oxutils/currency/enums.py +7 -0
- oxutils-0.1.5/src/oxutils/currency/migrations/0001_initial.py +41 -0
- oxutils-0.1.5/src/oxutils/currency/migrations/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/currency/models.py +100 -0
- oxutils-0.1.5/src/oxutils/currency/schemas.py +38 -0
- oxutils-0.1.5/src/oxutils/currency/tests.py +3 -0
- oxutils-0.1.5/src/oxutils/currency/utils.py +69 -0
- oxutils-0.1.5/src/oxutils/enums/__init__.py +1 -0
- oxutils-0.1.5/src/oxutils/enums/audit.py +8 -0
- oxutils-0.1.5/src/oxutils/enums/invoices.py +11 -0
- oxutils-0.1.5/src/oxutils/exceptions.py +117 -0
- oxutils-0.1.5/src/oxutils/functions.py +102 -0
- oxutils-0.1.5/src/oxutils/jwt/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/jwt/auth.py +55 -0
- oxutils-0.1.5/src/oxutils/jwt/client.py +123 -0
- oxutils-0.1.5/src/oxutils/jwt/constants.py +1 -0
- oxutils-0.1.5/src/oxutils/locale/fr/LC_MESSAGES/django.po +368 -0
- oxutils-0.1.5/src/oxutils/logger/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/logger/receivers.py +16 -0
- oxutils-0.1.5/src/oxutils/logger/settings.py +63 -0
- oxutils-0.1.5/src/oxutils/mixins/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/mixins/base.py +21 -0
- oxutils-0.1.5/src/oxutils/mixins/schemas.py +13 -0
- oxutils-0.1.5/src/oxutils/mixins/services.py +146 -0
- oxutils-0.1.5/src/oxutils/models/__init__.py +3 -0
- oxutils-0.1.5/src/oxutils/models/base.py +116 -0
- oxutils-0.1.5/src/oxutils/models/billing.py +140 -0
- oxutils-0.1.5/src/oxutils/models/invoice.py +467 -0
- oxutils-0.1.5/src/oxutils/oxiliere/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/oxiliere/admin.py +3 -0
- oxutils-0.1.5/src/oxutils/oxiliere/apps.py +6 -0
- oxutils-0.1.5/src/oxutils/oxiliere/cacheops.py +7 -0
- oxutils-0.1.5/src/oxutils/oxiliere/caches.py +33 -0
- oxutils-0.1.5/src/oxutils/oxiliere/controllers.py +36 -0
- oxutils-0.1.5/src/oxutils/oxiliere/enums.py +10 -0
- oxutils-0.1.5/src/oxutils/oxiliere/management/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/oxiliere/management/commands/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/oxiliere/management/commands/init_oxiliere_system.py +86 -0
- oxutils-0.1.5/src/oxutils/oxiliere/middleware.py +97 -0
- oxutils-0.1.5/src/oxutils/oxiliere/migrations/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/oxiliere/models.py +55 -0
- oxutils-0.1.5/src/oxutils/oxiliere/permissions.py +104 -0
- oxutils-0.1.5/src/oxutils/oxiliere/schemas.py +65 -0
- oxutils-0.1.5/src/oxutils/oxiliere/settings.py +17 -0
- oxutils-0.1.5/src/oxutils/oxiliere/tests.py +3 -0
- oxutils-0.1.5/src/oxutils/oxiliere/utils.py +76 -0
- oxutils-0.1.5/src/oxutils/pdf/__init__.py +10 -0
- oxutils-0.1.5/src/oxutils/pdf/printer.py +81 -0
- oxutils-0.1.5/src/oxutils/pdf/utils.py +94 -0
- oxutils-0.1.5/src/oxutils/pdf/views.py +208 -0
- oxutils-0.1.5/src/oxutils/py.typed +0 -0
- oxutils-0.1.5/src/oxutils/s3/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/s3/settings.py +34 -0
- oxutils-0.1.5/src/oxutils/s3/storages.py +130 -0
- oxutils-0.1.5/src/oxutils/settings.py +256 -0
- oxutils-0.1.5/src/oxutils/types.py +13 -0
- oxutils-0.1.5/src/oxutils/users/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/users/admin.py +3 -0
- oxutils-0.1.5/src/oxutils/users/apps.py +6 -0
- oxutils-0.1.5/src/oxutils/users/migrations/__init__.py +0 -0
- oxutils-0.1.5/src/oxutils/users/models.py +88 -0
- oxutils-0.1.5/src/oxutils/users/tests.py +3 -0
- oxutils-0.1.5/src/oxutils/users/utils.py +15 -0
oxutils-0.1.5/PKG-INFO
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oxutils
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: Production-ready utilities for Django applications in the Oxiliere ecosystem
|
|
5
|
+
Keywords: django,utilities,jwt,s3,audit,logging,celery,structlog
|
|
6
|
+
Author: Edimedia Mutoke
|
|
7
|
+
Author-email: Edimedia Mutoke <eddycondor07@gmail.com>
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Framework :: Django
|
|
11
|
+
Classifier: Framework :: Django :: 5.0
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
19
|
+
Requires-Dist: boto3>=1.41.5
|
|
20
|
+
Requires-Dist: celery>=5.5.3
|
|
21
|
+
Requires-Dist: cryptography>=46.0.3
|
|
22
|
+
Requires-Dist: django-auditlog>=3.3.0
|
|
23
|
+
Requires-Dist: django-celery-results>=2.6.0
|
|
24
|
+
Requires-Dist: django-extensions>=4.1
|
|
25
|
+
Requires-Dist: django-ninja>=1.5.0
|
|
26
|
+
Requires-Dist: django-ninja-extra>=0.30.6
|
|
27
|
+
Requires-Dist: django-storages[s3]>=1.14.6
|
|
28
|
+
Requires-Dist: django-structlog[celery]>=10.0.0
|
|
29
|
+
Requires-Dist: jwcrypto>=1.5.6
|
|
30
|
+
Requires-Dist: pydantic-settings>=2.12.0
|
|
31
|
+
Requires-Dist: pyjwt>=2.10.1
|
|
32
|
+
Requires-Dist: requests>=2.32.5
|
|
33
|
+
Requires-Python: >=3.12
|
|
34
|
+
Project-URL: Changelog, https://github.com/oxiliere/oxutils/blob/main/CHANGELOG.md
|
|
35
|
+
Project-URL: Documentation, https://github.com/oxiliere/oxutils/tree/main/docs
|
|
36
|
+
Project-URL: Homepage, https://github.com/oxiliere/oxutils
|
|
37
|
+
Project-URL: Issues, https://github.com/oxiliere/oxutils/issues
|
|
38
|
+
Project-URL: Repository, https://github.com/oxiliere/oxutils
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# OxUtils
|
|
42
|
+
|
|
43
|
+
**Production-ready utilities for Django applications in the Oxiliere ecosystem.**
|
|
44
|
+
|
|
45
|
+
[](https://pypi.org/project/oxutils/)
|
|
46
|
+
[](https://www.python.org/)
|
|
47
|
+
[](https://www.djangoproject.com/)
|
|
48
|
+
[](tests/)
|
|
49
|
+
[](LICENSE)
|
|
50
|
+
[](https://github.com/astral-sh/ruff)
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- 🔐 **JWT Authentication** - RS256 with JWKS caching
|
|
55
|
+
- 📦 **S3 Storage** - Static, media, private, and log backends
|
|
56
|
+
- 📝 **Structured Logging** - JSON logs with automatic request tracking
|
|
57
|
+
- 🔍 **Audit System** - Change tracking with S3 export
|
|
58
|
+
- ⚙️ **Celery Integration** - Pre-configured task processing
|
|
59
|
+
- 🛠️ **Django Mixins** - UUID, timestamps, user tracking
|
|
60
|
+
- ⚡ **Custom Exceptions** - Standardized API errors
|
|
61
|
+
- 🎨 **Context Processors** - Site name and domain for templates
|
|
62
|
+
- 💱 **Currency Module** - Multi-source exchange rates (BCC/OXR)
|
|
63
|
+
- 📄 **PDF Generation** - WeasyPrint integration for Django
|
|
64
|
+
- 🏢 **Multi-Tenant** - PostgreSQL schema-based isolation
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install oxutils
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uv add oxutils
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Quick Start
|
|
79
|
+
|
|
80
|
+
### 1. Configure Django Settings
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
# settings.py
|
|
84
|
+
from oxutils.conf import UTILS_APPS, AUDIT_MIDDLEWARE
|
|
85
|
+
|
|
86
|
+
INSTALLED_APPS = [
|
|
87
|
+
*UTILS_APPS, # structlog, auditlog, celery_results
|
|
88
|
+
# your apps...
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
MIDDLEWARE = [
|
|
92
|
+
*AUDIT_MIDDLEWARE, # RequestMiddleware, Auditlog
|
|
93
|
+
# your middleware...
|
|
94
|
+
]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 2. Environment Variables
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
OXI_SERVICE_NAME=my-service
|
|
101
|
+
OXI_JWT_JWKS_URL=https://auth.example.com/.well-known/jwks.json
|
|
102
|
+
OXI_USE_STATIC_S3=True
|
|
103
|
+
OXI_STATIC_STORAGE_BUCKET_NAME=my-bucket
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 3. Usage Examples
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# JWT Authentication
|
|
110
|
+
from oxutils.jwt.client import verify_token
|
|
111
|
+
payload = verify_token(token)
|
|
112
|
+
|
|
113
|
+
# Structured Logging
|
|
114
|
+
import structlog
|
|
115
|
+
logger = structlog.get_logger(__name__)
|
|
116
|
+
logger.info("user_action", user_id=user_id)
|
|
117
|
+
|
|
118
|
+
# S3 Storage
|
|
119
|
+
from oxutils.s3.storages import PrivateMediaStorage
|
|
120
|
+
class Document(models.Model):
|
|
121
|
+
file = models.FileField(storage=PrivateMediaStorage())
|
|
122
|
+
|
|
123
|
+
# Model Mixins
|
|
124
|
+
from oxutils.models.base import BaseModelMixin
|
|
125
|
+
class Product(BaseModelMixin): # UUID + timestamps + is_active
|
|
126
|
+
name = models.CharField(max_length=255)
|
|
127
|
+
|
|
128
|
+
# Custom Exceptions
|
|
129
|
+
from oxutils.exceptions import NotFoundException
|
|
130
|
+
raise NotFoundException(detail="User not found")
|
|
131
|
+
|
|
132
|
+
# Context Processors
|
|
133
|
+
# settings.py
|
|
134
|
+
TEMPLATES = [{
|
|
135
|
+
'OPTIONS': {
|
|
136
|
+
'context_processors': [
|
|
137
|
+
'oxutils.context.site_name_processor.site_name',
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
}]
|
|
141
|
+
# Now {{ site_name }} and {{ site_domain }} are available in templates
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Documentation
|
|
145
|
+
|
|
146
|
+
### Core Modules
|
|
147
|
+
- **[Settings](docs/settings.md)** - Configuration reference
|
|
148
|
+
- **[JWT](docs/jwt.md)** - Authentication
|
|
149
|
+
- **[S3](docs/s3.md)** - Storage backends
|
|
150
|
+
- **[Audit](docs/audit.md)** - Change tracking
|
|
151
|
+
- **[Logging](docs/logger.md)** - Structured logs
|
|
152
|
+
- **[Mixins](docs/mixins.md)** - Model/service mixins
|
|
153
|
+
- **[Celery](docs/celery.md)** - Task processing
|
|
154
|
+
|
|
155
|
+
### Additional Modules
|
|
156
|
+
- **[Currency](docs/currency.md)** - Exchange rates management
|
|
157
|
+
- **[PDF](docs/pdf.md)** - PDF generation with WeasyPrint
|
|
158
|
+
- **[Oxiliere](docs/oxiliere.md)** - Multi-tenant architecture
|
|
159
|
+
|
|
160
|
+
## Requirements
|
|
161
|
+
|
|
162
|
+
- Python 3.12+
|
|
163
|
+
- Django 5.0+
|
|
164
|
+
- PostgreSQL (recommended)
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
git clone https://github.com/oxiliere/oxutils.git
|
|
170
|
+
cd oxutils
|
|
171
|
+
uv sync
|
|
172
|
+
uv run pytest # 201 tests passing, 4 skipped
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Creating Migrations
|
|
176
|
+
|
|
177
|
+
To generate Django migrations for the audit module:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
make migrations
|
|
181
|
+
# or
|
|
182
|
+
uv run make_migrations.py
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
See [MIGRATIONS.md](MIGRATIONS.md) for detailed documentation.
|
|
186
|
+
|
|
187
|
+
## Optional Dependencies
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# Multi-tenant support
|
|
191
|
+
uv add oxutils[oxiliere]
|
|
192
|
+
|
|
193
|
+
# PDF generation
|
|
194
|
+
uv add oxutils[pdf]
|
|
195
|
+
|
|
196
|
+
# Development tools
|
|
197
|
+
uv add oxutils[dev]
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Advanced Examples
|
|
201
|
+
|
|
202
|
+
### JWT with Django Ninja
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from ninja import NinjaAPI
|
|
206
|
+
from ninja.security import HttpBearer
|
|
207
|
+
from oxutils.jwt.client import verify_token
|
|
208
|
+
|
|
209
|
+
class JWTAuth(HttpBearer):
|
|
210
|
+
def authenticate(self, request, token):
|
|
211
|
+
try:
|
|
212
|
+
return verify_token(token)
|
|
213
|
+
except:
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
api = NinjaAPI(auth=JWTAuth())
|
|
217
|
+
|
|
218
|
+
@api.get("/protected")
|
|
219
|
+
def protected(request):
|
|
220
|
+
return {"user_id": request.auth['sub']}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Audit Log Export
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from oxutils.audit.export import export_logs_from_date
|
|
227
|
+
from datetime import datetime, timedelta
|
|
228
|
+
|
|
229
|
+
from_date = datetime.now() - timedelta(days=7)
|
|
230
|
+
export = export_logs_from_date(from_date=from_date)
|
|
231
|
+
print(f"Exported to {export.data.url}")
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Currency Exchange Rates
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
from oxutils.currency.models import CurrencyState
|
|
238
|
+
|
|
239
|
+
# Sync rates from BCC (with OXR fallback)
|
|
240
|
+
state = CurrencyState.sync()
|
|
241
|
+
|
|
242
|
+
# Get latest rates
|
|
243
|
+
latest = CurrencyState.objects.latest()
|
|
244
|
+
usd_rate = latest.currencies.get(code='USD').rate
|
|
245
|
+
eur_rate = latest.currencies.get(code='EUR').rate
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### PDF Generation
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
from oxutils.pdf.printer import Printer
|
|
252
|
+
from oxutils.pdf.views import WeasyTemplateView
|
|
253
|
+
|
|
254
|
+
# Standalone PDF generation
|
|
255
|
+
printer = Printer(
|
|
256
|
+
template_name='invoice.html',
|
|
257
|
+
context={'invoice': invoice},
|
|
258
|
+
stylesheets=['css/invoice.css']
|
|
259
|
+
)
|
|
260
|
+
pdf_bytes = printer.write_pdf()
|
|
261
|
+
|
|
262
|
+
# Class-based view
|
|
263
|
+
class InvoicePDFView(WeasyTemplateView):
|
|
264
|
+
template_name = 'invoice.html'
|
|
265
|
+
pdf_filename = 'invoice.pdf'
|
|
266
|
+
pdf_stylesheets = ['css/invoice.css']
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Multi-Tenant Setup
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
# settings.py
|
|
273
|
+
TENANT_MODEL = "oxiliere.Tenant"
|
|
274
|
+
MIDDLEWARE = [
|
|
275
|
+
'oxutils.oxiliere.middleware.TenantMainMiddleware', # First!
|
|
276
|
+
# other middleware...
|
|
277
|
+
]
|
|
278
|
+
|
|
279
|
+
# All requests must include X-Organization-ID header
|
|
280
|
+
# Data is automatically isolated per tenant schema
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## License
|
|
284
|
+
|
|
285
|
+
Apache 2.0 License - see [LICENSE](LICENSE)
|
|
286
|
+
|
|
287
|
+
## Support
|
|
288
|
+
|
|
289
|
+
- **Issues**: [GitHub Issues](https://github.com/oxiliere/oxutils/issues)
|
|
290
|
+
- **Email**: dev@oxiliere.com
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
**Made with ❤️ by Oxiliere**
|
oxutils-0.1.5/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# OxUtils
|
|
2
|
+
|
|
3
|
+
**Production-ready utilities for Django applications in the Oxiliere ecosystem.**
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/oxutils/)
|
|
6
|
+
[](https://www.python.org/)
|
|
7
|
+
[](https://www.djangoproject.com/)
|
|
8
|
+
[](tests/)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://github.com/astral-sh/ruff)
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- 🔐 **JWT Authentication** - RS256 with JWKS caching
|
|
15
|
+
- 📦 **S3 Storage** - Static, media, private, and log backends
|
|
16
|
+
- 📝 **Structured Logging** - JSON logs with automatic request tracking
|
|
17
|
+
- 🔍 **Audit System** - Change tracking with S3 export
|
|
18
|
+
- ⚙️ **Celery Integration** - Pre-configured task processing
|
|
19
|
+
- 🛠️ **Django Mixins** - UUID, timestamps, user tracking
|
|
20
|
+
- ⚡ **Custom Exceptions** - Standardized API errors
|
|
21
|
+
- 🎨 **Context Processors** - Site name and domain for templates
|
|
22
|
+
- 💱 **Currency Module** - Multi-source exchange rates (BCC/OXR)
|
|
23
|
+
- 📄 **PDF Generation** - WeasyPrint integration for Django
|
|
24
|
+
- 🏢 **Multi-Tenant** - PostgreSQL schema-based isolation
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install oxutils
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv add oxutils
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### 1. Configure Django Settings
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
# settings.py
|
|
44
|
+
from oxutils.conf import UTILS_APPS, AUDIT_MIDDLEWARE
|
|
45
|
+
|
|
46
|
+
INSTALLED_APPS = [
|
|
47
|
+
*UTILS_APPS, # structlog, auditlog, celery_results
|
|
48
|
+
# your apps...
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
MIDDLEWARE = [
|
|
52
|
+
*AUDIT_MIDDLEWARE, # RequestMiddleware, Auditlog
|
|
53
|
+
# your middleware...
|
|
54
|
+
]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 2. Environment Variables
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
OXI_SERVICE_NAME=my-service
|
|
61
|
+
OXI_JWT_JWKS_URL=https://auth.example.com/.well-known/jwks.json
|
|
62
|
+
OXI_USE_STATIC_S3=True
|
|
63
|
+
OXI_STATIC_STORAGE_BUCKET_NAME=my-bucket
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 3. Usage Examples
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# JWT Authentication
|
|
70
|
+
from oxutils.jwt.client import verify_token
|
|
71
|
+
payload = verify_token(token)
|
|
72
|
+
|
|
73
|
+
# Structured Logging
|
|
74
|
+
import structlog
|
|
75
|
+
logger = structlog.get_logger(__name__)
|
|
76
|
+
logger.info("user_action", user_id=user_id)
|
|
77
|
+
|
|
78
|
+
# S3 Storage
|
|
79
|
+
from oxutils.s3.storages import PrivateMediaStorage
|
|
80
|
+
class Document(models.Model):
|
|
81
|
+
file = models.FileField(storage=PrivateMediaStorage())
|
|
82
|
+
|
|
83
|
+
# Model Mixins
|
|
84
|
+
from oxutils.models.base import BaseModelMixin
|
|
85
|
+
class Product(BaseModelMixin): # UUID + timestamps + is_active
|
|
86
|
+
name = models.CharField(max_length=255)
|
|
87
|
+
|
|
88
|
+
# Custom Exceptions
|
|
89
|
+
from oxutils.exceptions import NotFoundException
|
|
90
|
+
raise NotFoundException(detail="User not found")
|
|
91
|
+
|
|
92
|
+
# Context Processors
|
|
93
|
+
# settings.py
|
|
94
|
+
TEMPLATES = [{
|
|
95
|
+
'OPTIONS': {
|
|
96
|
+
'context_processors': [
|
|
97
|
+
'oxutils.context.site_name_processor.site_name',
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
}]
|
|
101
|
+
# Now {{ site_name }} and {{ site_domain }} are available in templates
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Documentation
|
|
105
|
+
|
|
106
|
+
### Core Modules
|
|
107
|
+
- **[Settings](docs/settings.md)** - Configuration reference
|
|
108
|
+
- **[JWT](docs/jwt.md)** - Authentication
|
|
109
|
+
- **[S3](docs/s3.md)** - Storage backends
|
|
110
|
+
- **[Audit](docs/audit.md)** - Change tracking
|
|
111
|
+
- **[Logging](docs/logger.md)** - Structured logs
|
|
112
|
+
- **[Mixins](docs/mixins.md)** - Model/service mixins
|
|
113
|
+
- **[Celery](docs/celery.md)** - Task processing
|
|
114
|
+
|
|
115
|
+
### Additional Modules
|
|
116
|
+
- **[Currency](docs/currency.md)** - Exchange rates management
|
|
117
|
+
- **[PDF](docs/pdf.md)** - PDF generation with WeasyPrint
|
|
118
|
+
- **[Oxiliere](docs/oxiliere.md)** - Multi-tenant architecture
|
|
119
|
+
|
|
120
|
+
## Requirements
|
|
121
|
+
|
|
122
|
+
- Python 3.12+
|
|
123
|
+
- Django 5.0+
|
|
124
|
+
- PostgreSQL (recommended)
|
|
125
|
+
|
|
126
|
+
## Development
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
git clone https://github.com/oxiliere/oxutils.git
|
|
130
|
+
cd oxutils
|
|
131
|
+
uv sync
|
|
132
|
+
uv run pytest # 201 tests passing, 4 skipped
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Creating Migrations
|
|
136
|
+
|
|
137
|
+
To generate Django migrations for the audit module:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
make migrations
|
|
141
|
+
# or
|
|
142
|
+
uv run make_migrations.py
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
See [MIGRATIONS.md](MIGRATIONS.md) for detailed documentation.
|
|
146
|
+
|
|
147
|
+
## Optional Dependencies
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Multi-tenant support
|
|
151
|
+
uv add oxutils[oxiliere]
|
|
152
|
+
|
|
153
|
+
# PDF generation
|
|
154
|
+
uv add oxutils[pdf]
|
|
155
|
+
|
|
156
|
+
# Development tools
|
|
157
|
+
uv add oxutils[dev]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Advanced Examples
|
|
161
|
+
|
|
162
|
+
### JWT with Django Ninja
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from ninja import NinjaAPI
|
|
166
|
+
from ninja.security import HttpBearer
|
|
167
|
+
from oxutils.jwt.client import verify_token
|
|
168
|
+
|
|
169
|
+
class JWTAuth(HttpBearer):
|
|
170
|
+
def authenticate(self, request, token):
|
|
171
|
+
try:
|
|
172
|
+
return verify_token(token)
|
|
173
|
+
except:
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
api = NinjaAPI(auth=JWTAuth())
|
|
177
|
+
|
|
178
|
+
@api.get("/protected")
|
|
179
|
+
def protected(request):
|
|
180
|
+
return {"user_id": request.auth['sub']}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Audit Log Export
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from oxutils.audit.export import export_logs_from_date
|
|
187
|
+
from datetime import datetime, timedelta
|
|
188
|
+
|
|
189
|
+
from_date = datetime.now() - timedelta(days=7)
|
|
190
|
+
export = export_logs_from_date(from_date=from_date)
|
|
191
|
+
print(f"Exported to {export.data.url}")
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Currency Exchange Rates
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from oxutils.currency.models import CurrencyState
|
|
198
|
+
|
|
199
|
+
# Sync rates from BCC (with OXR fallback)
|
|
200
|
+
state = CurrencyState.sync()
|
|
201
|
+
|
|
202
|
+
# Get latest rates
|
|
203
|
+
latest = CurrencyState.objects.latest()
|
|
204
|
+
usd_rate = latest.currencies.get(code='USD').rate
|
|
205
|
+
eur_rate = latest.currencies.get(code='EUR').rate
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### PDF Generation
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from oxutils.pdf.printer import Printer
|
|
212
|
+
from oxutils.pdf.views import WeasyTemplateView
|
|
213
|
+
|
|
214
|
+
# Standalone PDF generation
|
|
215
|
+
printer = Printer(
|
|
216
|
+
template_name='invoice.html',
|
|
217
|
+
context={'invoice': invoice},
|
|
218
|
+
stylesheets=['css/invoice.css']
|
|
219
|
+
)
|
|
220
|
+
pdf_bytes = printer.write_pdf()
|
|
221
|
+
|
|
222
|
+
# Class-based view
|
|
223
|
+
class InvoicePDFView(WeasyTemplateView):
|
|
224
|
+
template_name = 'invoice.html'
|
|
225
|
+
pdf_filename = 'invoice.pdf'
|
|
226
|
+
pdf_stylesheets = ['css/invoice.css']
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Multi-Tenant Setup
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
# settings.py
|
|
233
|
+
TENANT_MODEL = "oxiliere.Tenant"
|
|
234
|
+
MIDDLEWARE = [
|
|
235
|
+
'oxutils.oxiliere.middleware.TenantMainMiddleware', # First!
|
|
236
|
+
# other middleware...
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
# All requests must include X-Organization-ID header
|
|
240
|
+
# Data is automatically isolated per tenant schema
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## License
|
|
244
|
+
|
|
245
|
+
Apache 2.0 License - see [LICENSE](LICENSE)
|
|
246
|
+
|
|
247
|
+
## Support
|
|
248
|
+
|
|
249
|
+
- **Issues**: [GitHub Issues](https://github.com/oxiliere/oxutils/issues)
|
|
250
|
+
- **Email**: dev@oxiliere.com
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
**Made with ❤️ by Oxiliere**
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "oxutils"
|
|
3
|
+
version = "0.1.5"
|
|
4
|
+
description = "Production-ready utilities for Django applications in the Oxiliere ecosystem"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "Apache-2.0"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Edimedia Mutoke", email = "eddycondor07@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
keywords = ["django", "utilities", "jwt", "s3", "audit", "logging", "celery", "structlog"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 4 - Beta",
|
|
14
|
+
"Framework :: Django",
|
|
15
|
+
"Framework :: Django :: 5.0",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: Apache Software License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"boto3>=1.41.5",
|
|
26
|
+
"celery>=5.5.3",
|
|
27
|
+
"cryptography>=46.0.3",
|
|
28
|
+
"django-auditlog>=3.3.0",
|
|
29
|
+
"django-celery-results>=2.6.0",
|
|
30
|
+
"django-extensions>=4.1",
|
|
31
|
+
"django-ninja>=1.5.0",
|
|
32
|
+
"django-ninja-extra>=0.30.6",
|
|
33
|
+
"django-storages[s3]>=1.14.6",
|
|
34
|
+
"django-structlog[celery]>=10.0.0",
|
|
35
|
+
"jwcrypto>=1.5.6",
|
|
36
|
+
"pydantic-settings>=2.12.0",
|
|
37
|
+
"pyjwt>=2.10.1",
|
|
38
|
+
"requests>=2.32.5",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/oxiliere/oxutils"
|
|
43
|
+
Documentation = "https://github.com/oxiliere/oxutils/tree/main/docs"
|
|
44
|
+
Repository = "https://github.com/oxiliere/oxutils"
|
|
45
|
+
Issues = "https://github.com/oxiliere/oxutils/issues"
|
|
46
|
+
Changelog = "https://github.com/oxiliere/oxutils/blob/main/CHANGELOG.md"
|
|
47
|
+
|
|
48
|
+
[build-system]
|
|
49
|
+
requires = ["uv_build>=0.8.13,<0.9.0"]
|
|
50
|
+
build-backend = "uv_build"
|
|
51
|
+
|
|
52
|
+
[tool.uv.workspace]
|
|
53
|
+
members = [
|
|
54
|
+
".",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[tool.uv.sources]
|
|
58
|
+
oxutils = { workspace = true }
|
|
59
|
+
|
|
60
|
+
[dependency-groups]
|
|
61
|
+
currency = [
|
|
62
|
+
"bcc-rates>=1.1.0",
|
|
63
|
+
]
|
|
64
|
+
dev = [
|
|
65
|
+
"pytest>=8.0.0",
|
|
66
|
+
"pytest-django>=4.8.0",
|
|
67
|
+
"pytest-cov>=4.1.0",
|
|
68
|
+
"pytest-mock>=3.12.0",
|
|
69
|
+
"coverage>=7.4.0",
|
|
70
|
+
"pillow>=12.0.0",
|
|
71
|
+
"ruff>=0.8.0",
|
|
72
|
+
]
|
|
73
|
+
oxiliere = [
|
|
74
|
+
"django-cacheops>=7.2",
|
|
75
|
+
"django-tenants>=3.9.0",
|
|
76
|
+
]
|
|
77
|
+
pdf = [
|
|
78
|
+
"weasyprint>=67.0",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[tool.ruff]
|
|
82
|
+
line-length = 100
|
|
83
|
+
target-version = "py312"
|
|
84
|
+
exclude = [
|
|
85
|
+
".git",
|
|
86
|
+
".venv",
|
|
87
|
+
"__pycache__",
|
|
88
|
+
"build",
|
|
89
|
+
"dist",
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
[tool.ruff.lint]
|
|
93
|
+
select = [
|
|
94
|
+
"E", # pycodestyle errors
|
|
95
|
+
"W", # pycodestyle warnings
|
|
96
|
+
"F", # pyflakes
|
|
97
|
+
"I", # isort
|
|
98
|
+
"B", # flake8-bugbear
|
|
99
|
+
"C4", # flake8-comprehensions
|
|
100
|
+
]
|
|
101
|
+
ignore = [
|
|
102
|
+
"E501", # line too long (handled by formatter)
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
[tool.pytest.ini_options]
|
|
106
|
+
pythonpath = [".", "src"]
|
|
107
|
+
python_files = ["test_*.py"]
|
|
108
|
+
python_classes = ["Test*"]
|
|
109
|
+
python_functions = ["test_*"]
|
|
110
|
+
addopts = "-v --tb=short --ds=tests.settings"
|
|
111
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""OxUtils - Production-ready utilities for Django applications.
|
|
2
|
+
|
|
3
|
+
This package provides:
|
|
4
|
+
- JWT authentication with JWKS support
|
|
5
|
+
- S3 storage backends (static, media, private, logs)
|
|
6
|
+
- Structured logging with correlation IDs
|
|
7
|
+
- Audit system with S3 export
|
|
8
|
+
- Celery integration
|
|
9
|
+
- Django model mixins
|
|
10
|
+
- Custom exceptions
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.5"
|
|
14
|
+
|
|
15
|
+
from oxutils.settings import oxi_settings
|
|
16
|
+
from oxutils.conf import UTILS_APPS, AUDIT_MIDDLEWARE
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"oxi_settings",
|
|
20
|
+
"UTILS_APPS",
|
|
21
|
+
"AUDIT_MIDDLEWARE",
|
|
22
|
+
"__version__",
|
|
23
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from django.apps import AppConfig
|
|
2
|
+
from django.utils.translation import gettext_lazy as _
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class OxutilsConfig(AppConfig):
|
|
7
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
8
|
+
name = 'oxutils'
|
|
9
|
+
verbose_name = _("Oxiliere Utilities")
|
|
10
|
+
|
|
11
|
+
def ready(self):
|
|
12
|
+
import oxutils.logger.receivers
|
|
13
|
+
|
|
14
|
+
return super().ready()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Oxutils Audit Module
|
|
3
|
+
|
|
4
|
+
Provides audit log export functionality with S3 storage.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# Models are imported lazily to avoid AppRegistryNotReady errors
|
|
8
|
+
# Use: from oxutils.audit.models import LogExportState, LogExportHistory
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
'LogExportState',
|
|
12
|
+
'LogExportHistory',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
def __getattr__(name):
|
|
16
|
+
"""Lazy import of models to avoid AppRegistryNotReady errors."""
|
|
17
|
+
if name in __all__:
|
|
18
|
+
from oxutils.audit.models import LogExportState, LogExportHistory
|
|
19
|
+
return locals()[name]
|
|
20
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|