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.
Files changed (88) hide show
  1. oxutils-0.1.5/PKG-INFO +294 -0
  2. oxutils-0.1.5/README.md +254 -0
  3. oxutils-0.1.5/pyproject.toml +111 -0
  4. oxutils-0.1.5/src/oxutils/__init__.py +23 -0
  5. oxutils-0.1.5/src/oxutils/apps.py +14 -0
  6. oxutils-0.1.5/src/oxutils/audit/__init__.py +20 -0
  7. oxutils-0.1.5/src/oxutils/audit/apps.py +12 -0
  8. oxutils-0.1.5/src/oxutils/audit/export.py +229 -0
  9. oxutils-0.1.5/src/oxutils/audit/masks.py +97 -0
  10. oxutils-0.1.5/src/oxutils/audit/migrations/0001_initial.py +41 -0
  11. oxutils-0.1.5/src/oxutils/audit/migrations/__init__.py +0 -0
  12. oxutils-0.1.5/src/oxutils/audit/models.py +75 -0
  13. oxutils-0.1.5/src/oxutils/audit/settings.py +4 -0
  14. oxutils-0.1.5/src/oxutils/audit/utils.py +22 -0
  15. oxutils-0.1.5/src/oxutils/celery/__init__.py +1 -0
  16. oxutils-0.1.5/src/oxutils/celery/base.py +98 -0
  17. oxutils-0.1.5/src/oxutils/celery/settings.py +1 -0
  18. oxutils-0.1.5/src/oxutils/conf.py +11 -0
  19. oxutils-0.1.5/src/oxutils/constants.py +2 -0
  20. oxutils-0.1.5/src/oxutils/context/__init__.py +0 -0
  21. oxutils-0.1.5/src/oxutils/context/site_name_processor.py +11 -0
  22. oxutils-0.1.5/src/oxutils/currency/__init__.py +0 -0
  23. oxutils-0.1.5/src/oxutils/currency/admin.py +57 -0
  24. oxutils-0.1.5/src/oxutils/currency/apps.py +7 -0
  25. oxutils-0.1.5/src/oxutils/currency/controllers.py +79 -0
  26. oxutils-0.1.5/src/oxutils/currency/enums.py +7 -0
  27. oxutils-0.1.5/src/oxutils/currency/migrations/0001_initial.py +41 -0
  28. oxutils-0.1.5/src/oxutils/currency/migrations/__init__.py +0 -0
  29. oxutils-0.1.5/src/oxutils/currency/models.py +100 -0
  30. oxutils-0.1.5/src/oxutils/currency/schemas.py +38 -0
  31. oxutils-0.1.5/src/oxutils/currency/tests.py +3 -0
  32. oxutils-0.1.5/src/oxutils/currency/utils.py +69 -0
  33. oxutils-0.1.5/src/oxutils/enums/__init__.py +1 -0
  34. oxutils-0.1.5/src/oxutils/enums/audit.py +8 -0
  35. oxutils-0.1.5/src/oxutils/enums/invoices.py +11 -0
  36. oxutils-0.1.5/src/oxutils/exceptions.py +117 -0
  37. oxutils-0.1.5/src/oxutils/functions.py +102 -0
  38. oxutils-0.1.5/src/oxutils/jwt/__init__.py +0 -0
  39. oxutils-0.1.5/src/oxutils/jwt/auth.py +55 -0
  40. oxutils-0.1.5/src/oxutils/jwt/client.py +123 -0
  41. oxutils-0.1.5/src/oxutils/jwt/constants.py +1 -0
  42. oxutils-0.1.5/src/oxutils/locale/fr/LC_MESSAGES/django.po +368 -0
  43. oxutils-0.1.5/src/oxutils/logger/__init__.py +0 -0
  44. oxutils-0.1.5/src/oxutils/logger/receivers.py +16 -0
  45. oxutils-0.1.5/src/oxutils/logger/settings.py +63 -0
  46. oxutils-0.1.5/src/oxutils/mixins/__init__.py +0 -0
  47. oxutils-0.1.5/src/oxutils/mixins/base.py +21 -0
  48. oxutils-0.1.5/src/oxutils/mixins/schemas.py +13 -0
  49. oxutils-0.1.5/src/oxutils/mixins/services.py +146 -0
  50. oxutils-0.1.5/src/oxutils/models/__init__.py +3 -0
  51. oxutils-0.1.5/src/oxutils/models/base.py +116 -0
  52. oxutils-0.1.5/src/oxutils/models/billing.py +140 -0
  53. oxutils-0.1.5/src/oxutils/models/invoice.py +467 -0
  54. oxutils-0.1.5/src/oxutils/oxiliere/__init__.py +0 -0
  55. oxutils-0.1.5/src/oxutils/oxiliere/admin.py +3 -0
  56. oxutils-0.1.5/src/oxutils/oxiliere/apps.py +6 -0
  57. oxutils-0.1.5/src/oxutils/oxiliere/cacheops.py +7 -0
  58. oxutils-0.1.5/src/oxutils/oxiliere/caches.py +33 -0
  59. oxutils-0.1.5/src/oxutils/oxiliere/controllers.py +36 -0
  60. oxutils-0.1.5/src/oxutils/oxiliere/enums.py +10 -0
  61. oxutils-0.1.5/src/oxutils/oxiliere/management/__init__.py +0 -0
  62. oxutils-0.1.5/src/oxutils/oxiliere/management/commands/__init__.py +0 -0
  63. oxutils-0.1.5/src/oxutils/oxiliere/management/commands/init_oxiliere_system.py +86 -0
  64. oxutils-0.1.5/src/oxutils/oxiliere/middleware.py +97 -0
  65. oxutils-0.1.5/src/oxutils/oxiliere/migrations/__init__.py +0 -0
  66. oxutils-0.1.5/src/oxutils/oxiliere/models.py +55 -0
  67. oxutils-0.1.5/src/oxutils/oxiliere/permissions.py +104 -0
  68. oxutils-0.1.5/src/oxutils/oxiliere/schemas.py +65 -0
  69. oxutils-0.1.5/src/oxutils/oxiliere/settings.py +17 -0
  70. oxutils-0.1.5/src/oxutils/oxiliere/tests.py +3 -0
  71. oxutils-0.1.5/src/oxutils/oxiliere/utils.py +76 -0
  72. oxutils-0.1.5/src/oxutils/pdf/__init__.py +10 -0
  73. oxutils-0.1.5/src/oxutils/pdf/printer.py +81 -0
  74. oxutils-0.1.5/src/oxutils/pdf/utils.py +94 -0
  75. oxutils-0.1.5/src/oxutils/pdf/views.py +208 -0
  76. oxutils-0.1.5/src/oxutils/py.typed +0 -0
  77. oxutils-0.1.5/src/oxutils/s3/__init__.py +0 -0
  78. oxutils-0.1.5/src/oxutils/s3/settings.py +34 -0
  79. oxutils-0.1.5/src/oxutils/s3/storages.py +130 -0
  80. oxutils-0.1.5/src/oxutils/settings.py +256 -0
  81. oxutils-0.1.5/src/oxutils/types.py +13 -0
  82. oxutils-0.1.5/src/oxutils/users/__init__.py +0 -0
  83. oxutils-0.1.5/src/oxutils/users/admin.py +3 -0
  84. oxutils-0.1.5/src/oxutils/users/apps.py +6 -0
  85. oxutils-0.1.5/src/oxutils/users/migrations/__init__.py +0 -0
  86. oxutils-0.1.5/src/oxutils/users/models.py +88 -0
  87. oxutils-0.1.5/src/oxutils/users/tests.py +3 -0
  88. 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
+ [![PyPI version](https://img.shields.io/pypi/v/oxutils.svg)](https://pypi.org/project/oxutils/)
46
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/)
47
+ [![Django 5.0+](https://img.shields.io/badge/django-5.0+-green.svg)](https://www.djangoproject.com/)
48
+ [![Tests](https://img.shields.io/badge/tests-201%20passed-success.svg)](tests/)
49
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
50
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](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**
@@ -0,0 +1,254 @@
1
+ # OxUtils
2
+
3
+ **Production-ready utilities for Django applications in the Oxiliere ecosystem.**
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/oxutils.svg)](https://pypi.org/project/oxutils/)
6
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/)
7
+ [![Django 5.0+](https://img.shields.io/badge/django-5.0+-green.svg)](https://www.djangoproject.com/)
8
+ [![Tests](https://img.shields.io/badge/tests-201%20passed-success.svg)](tests/)
9
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
10
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](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}")