oxutils 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
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/__init__.py +1 -1
- oxutils/context/__init__.py +0 -0
- oxutils/context/site_name_processor.py +11 -0
- oxutils/functions.py +5 -2
- oxutils/s3/storages.py +4 -4
- oxutils/settings.py +15 -13
- {oxutils-0.1.1.dist-info → oxutils-0.1.3.dist-info}/METADATA +15 -3
- {oxutils-0.1.1.dist-info → oxutils-0.1.3.dist-info}/RECORD +9 -8
- {oxutils-0.1.1.dist-info → oxutils-0.1.3.dist-info}/WHEEL +1 -1
- oxutils/locale/fr/LC_MESSAGES/django.mo +0 -0
oxutils/__init__.py
CHANGED
|
File without changes
|
oxutils/functions.py
CHANGED
|
@@ -6,12 +6,15 @@ from ninja_extra.exceptions import ValidationError
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def get_absolute_url(url: str, request=None):
|
|
9
|
+
if url.startswith('http'):
|
|
10
|
+
return url
|
|
11
|
+
|
|
9
12
|
if request:
|
|
10
13
|
# Build absolute URL using request
|
|
11
14
|
return request.build_absolute_uri(url)
|
|
12
15
|
else:
|
|
13
|
-
# Fallback: build URL using
|
|
14
|
-
base_url = getattr(settings, '
|
|
16
|
+
# Fallback: build URL using SITE_DOMAIN and domain
|
|
17
|
+
base_url = getattr(settings, 'SITE_DOMAIN', 'http://localhost:8000')
|
|
15
18
|
return urljoin(base_url, url)
|
|
16
19
|
|
|
17
20
|
|
oxutils/s3/storages.py
CHANGED
|
@@ -53,7 +53,7 @@ class PublicMediaStorage(S3Boto3Storage):
|
|
|
53
53
|
self.access_key = oxi_settings.default_s3_access_key_id
|
|
54
54
|
self.secret_key = oxi_settings.default_s3_secret_access_key
|
|
55
55
|
self.bucket_name = oxi_settings.default_s3_storage_bucket_name
|
|
56
|
-
self.custom_domain = oxi_settings.
|
|
56
|
+
self.custom_domain = oxi_settings.default_s3_custom_domain
|
|
57
57
|
|
|
58
58
|
self.location = oxi_settings.default_s3_location
|
|
59
59
|
self.default_acl = oxi_settings.default_s3_default_acl
|
|
@@ -79,7 +79,7 @@ class PrivateMediaStorage(S3Boto3Storage):
|
|
|
79
79
|
self.access_key = oxi_settings.private_s3_access_key_id
|
|
80
80
|
self.secret_key = oxi_settings.private_s3_secret_access_key
|
|
81
81
|
self.bucket_name = oxi_settings.private_s3_storage_bucket_name
|
|
82
|
-
self.custom_domain = oxi_settings.
|
|
82
|
+
self.custom_domain = oxi_settings.private_s3_custom_domain
|
|
83
83
|
self.location = oxi_settings.private_s3_location
|
|
84
84
|
self.default_acl = oxi_settings.private_s3_default_acl
|
|
85
85
|
self.file_overwrite = False
|
|
@@ -107,12 +107,12 @@ class LogStorage(S3Boto3Storage):
|
|
|
107
107
|
self.access_key = oxi_settings.private_s3_access_key_id
|
|
108
108
|
self.secret_key = oxi_settings.private_s3_secret_access_key
|
|
109
109
|
self.bucket_name = oxi_settings.private_s3_storage_bucket_name
|
|
110
|
-
self.custom_domain = oxi_settings.
|
|
110
|
+
self.custom_domain = oxi_settings.private_s3_custom_domain
|
|
111
111
|
else:
|
|
112
112
|
self.access_key = oxi_settings.log_s3_access_key_id
|
|
113
113
|
self.secret_key = oxi_settings.log_s3_secret_access_key
|
|
114
114
|
self.bucket_name = oxi_settings.log_s3_storage_bucket_name
|
|
115
|
-
self.custom_domain = oxi_settings.
|
|
115
|
+
self.custom_domain = oxi_settings.log_s3_custom_domain
|
|
116
116
|
|
|
117
117
|
self.location = f'{oxi_settings.log_s3_location}/{oxi_settings.service_name}'
|
|
118
118
|
self.default_acl = oxi_settings.log_s3_default_acl
|
oxutils/settings.py
CHANGED
|
@@ -21,6 +21,8 @@ class OxUtilsSettings(BaseSettings):
|
|
|
21
21
|
|
|
22
22
|
# Service
|
|
23
23
|
service_name: Optional[str] = 'Oxutils'
|
|
24
|
+
site_name: Optional[str] = 'Oxiliere'
|
|
25
|
+
site_domain: Optional[str] = 'oxiliere.com'
|
|
24
26
|
|
|
25
27
|
# Auth JWT Settings (JWT_SIGNING_KEY)
|
|
26
28
|
jwt_signing_key: Optional[str] = None
|
|
@@ -52,7 +54,7 @@ class OxUtilsSettings(BaseSettings):
|
|
|
52
54
|
default_s3_secret_access_key: Optional[str] = None
|
|
53
55
|
default_s3_storage_bucket_name: Optional[str] = None
|
|
54
56
|
default_s3_default_acl: str = Field('public-read')
|
|
55
|
-
|
|
57
|
+
default_s3_custom_domain: Optional[str] = None
|
|
56
58
|
default_s3_location: str = Field('media')
|
|
57
59
|
default_s3_storage: str = Field('oxutils.s3.storages.PublicMediaStorage')
|
|
58
60
|
|
|
@@ -62,7 +64,7 @@ class OxUtilsSettings(BaseSettings):
|
|
|
62
64
|
private_s3_secret_access_key: Optional[str] = None
|
|
63
65
|
private_s3_storage_bucket_name: Optional[str] = None
|
|
64
66
|
private_s3_default_acl: str = Field('private')
|
|
65
|
-
|
|
67
|
+
private_s3_custom_domain: Optional[str] = None
|
|
66
68
|
private_s3_location: str = Field('private')
|
|
67
69
|
private_s3_storage: str = Field('oxutils.s3.storages.PrivateMediaStorage')
|
|
68
70
|
|
|
@@ -73,7 +75,7 @@ class OxUtilsSettings(BaseSettings):
|
|
|
73
75
|
log_s3_secret_access_key: Optional[str] = None
|
|
74
76
|
log_s3_storage_bucket_name: Optional[str] = None
|
|
75
77
|
log_s3_default_acl: str = Field('private')
|
|
76
|
-
|
|
78
|
+
log_s3_custom_domain: Optional[str] = None
|
|
77
79
|
log_s3_location: str = Field('oxi_logs')
|
|
78
80
|
log_s3_storage: str = Field('oxutils.s3.storages.LogStorage')
|
|
79
81
|
|
|
@@ -102,7 +104,7 @@ class OxUtilsSettings(BaseSettings):
|
|
|
102
104
|
self.default_s3_access_key_id,
|
|
103
105
|
self.default_s3_secret_access_key,
|
|
104
106
|
self.default_s3_storage_bucket_name,
|
|
105
|
-
self.
|
|
107
|
+
self.default_s3_custom_domain
|
|
106
108
|
)
|
|
107
109
|
elif not self.use_static_s3:
|
|
108
110
|
raise ValueError(
|
|
@@ -116,7 +118,7 @@ class OxUtilsSettings(BaseSettings):
|
|
|
116
118
|
self.private_s3_access_key_id,
|
|
117
119
|
self.private_s3_secret_access_key,
|
|
118
120
|
self.private_s3_storage_bucket_name,
|
|
119
|
-
self.
|
|
121
|
+
self.private_s3_custom_domain
|
|
120
122
|
)
|
|
121
123
|
|
|
122
124
|
# Validate log S3
|
|
@@ -127,7 +129,7 @@ class OxUtilsSettings(BaseSettings):
|
|
|
127
129
|
self.log_s3_access_key_id,
|
|
128
130
|
self.log_s3_secret_access_key,
|
|
129
131
|
self.log_s3_storage_bucket_name,
|
|
130
|
-
self.
|
|
132
|
+
self.log_s3_custom_domain
|
|
131
133
|
)
|
|
132
134
|
elif not self.use_private_s3:
|
|
133
135
|
raise ValueError(
|
|
@@ -166,11 +168,11 @@ class OxUtilsSettings(BaseSettings):
|
|
|
166
168
|
"""Validate required S3 configuration fields."""
|
|
167
169
|
missing_fields = []
|
|
168
170
|
if not access_key:
|
|
169
|
-
missing_fields.append(f'OXI_{name.upper()}
|
|
171
|
+
missing_fields.append(f'OXI_{name.upper()}_S3_ACCESS_KEY_ID')
|
|
170
172
|
if not secret_key:
|
|
171
|
-
missing_fields.append(f'OXI_{name.upper()}
|
|
173
|
+
missing_fields.append(f'OXI_{name.upper()}_S3_SECRET_ACCESS_KEY')
|
|
172
174
|
if not bucket:
|
|
173
|
-
missing_fields.append(f'OXI_{name.upper()}
|
|
175
|
+
missing_fields.append(f'OXI_{name.upper()}_S3_STORAGE_BUCKET_NAME')
|
|
174
176
|
if not domain:
|
|
175
177
|
missing_fields.append(f'OXI_{name.upper()}_S3_CUSTOM_DOMAIN')
|
|
176
178
|
|
|
@@ -194,7 +196,7 @@ class OxUtilsSettings(BaseSettings):
|
|
|
194
196
|
# Use static S3 credentials but keep default_s3 specific values (location, etc.)
|
|
195
197
|
domain = self.static_s3_custom_domain
|
|
196
198
|
else:
|
|
197
|
-
domain = self.
|
|
199
|
+
domain = self.default_s3_custom_domain
|
|
198
200
|
return f'https://{domain}/{self.default_s3_location}/'
|
|
199
201
|
|
|
200
202
|
raise ImproperlyConfigured(
|
|
@@ -207,7 +209,7 @@ class OxUtilsSettings(BaseSettings):
|
|
|
207
209
|
raise ImproperlyConfigured(
|
|
208
210
|
"Private S3 is not enabled. Set OXI_USE_PRIVATE_S3=True."
|
|
209
211
|
)
|
|
210
|
-
return f'https://{self.
|
|
212
|
+
return f'https://{self.private_s3_custom_domain}/{self.private_s3_location}/'
|
|
211
213
|
|
|
212
214
|
def get_log_storage_url(self) -> str:
|
|
213
215
|
"""Get log storage URL."""
|
|
@@ -217,9 +219,9 @@ class OxUtilsSettings(BaseSettings):
|
|
|
217
219
|
)
|
|
218
220
|
if self.use_private_s3_as_log:
|
|
219
221
|
# Use private S3 credentials but keep log_s3 specific values (location, etc.)
|
|
220
|
-
domain = self.
|
|
222
|
+
domain = self.private_s3_custom_domain
|
|
221
223
|
else:
|
|
222
|
-
domain = self.
|
|
224
|
+
domain = self.log_s3_custom_domain
|
|
223
225
|
return f'https://{domain}/{self.log_s3_location}/{self.service_name}/'
|
|
224
226
|
|
|
225
227
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oxutils
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Production-ready utilities for Django applications in the Oxiliere ecosystem
|
|
5
5
|
Keywords: django,utilities,jwt,s3,audit,logging,celery,structlog
|
|
6
6
|
Author: Edimedia Mutoke
|
|
@@ -47,7 +47,7 @@ Description-Content-Type: text/markdown
|
|
|
47
47
|
[](https://pypi.org/project/oxutils/)
|
|
48
48
|
[](https://www.python.org/)
|
|
49
49
|
[](https://www.djangoproject.com/)
|
|
50
|
-
[](tests/)
|
|
51
51
|
[](LICENSE)
|
|
52
52
|
[](https://github.com/astral-sh/ruff)
|
|
53
53
|
|
|
@@ -60,6 +60,7 @@ Description-Content-Type: text/markdown
|
|
|
60
60
|
- ⚙️ **Celery Integration** - Pre-configured task processing
|
|
61
61
|
- 🛠️ **Django Mixins** - UUID, timestamps, user tracking
|
|
62
62
|
- ⚡ **Custom Exceptions** - Standardized API errors
|
|
63
|
+
- 🎨 **Context Processors** - Site name and domain for templates
|
|
63
64
|
|
|
64
65
|
---
|
|
65
66
|
|
|
@@ -126,6 +127,17 @@ class Product(BaseModelMixin): # UUID + timestamps + is_active
|
|
|
126
127
|
# Custom Exceptions
|
|
127
128
|
from oxutils.exceptions import NotFoundException
|
|
128
129
|
raise NotFoundException(detail="User not found")
|
|
130
|
+
|
|
131
|
+
# Context Processors
|
|
132
|
+
# settings.py
|
|
133
|
+
TEMPLATES = [{
|
|
134
|
+
'OPTIONS': {
|
|
135
|
+
'context_processors': [
|
|
136
|
+
'oxutils.context.site_name_processor.site_name',
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
}]
|
|
140
|
+
# Now {{ site_name }} and {{ site_domain }} are available in templates
|
|
129
141
|
```
|
|
130
142
|
|
|
131
143
|
## Documentation
|
|
@@ -150,7 +162,7 @@ raise NotFoundException(detail="User not found")
|
|
|
150
162
|
git clone https://github.com/oxiliere/oxutils.git
|
|
151
163
|
cd oxutils
|
|
152
164
|
uv sync
|
|
153
|
-
uv run pytest #
|
|
165
|
+
uv run pytest # 145 tests
|
|
154
166
|
```
|
|
155
167
|
|
|
156
168
|
### Creating Migrations
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
oxutils/__init__.py,sha256=
|
|
1
|
+
oxutils/__init__.py,sha256=O_DKaIil9dbZtBsTRsICXaymyDp7zz_4eqqO2fO0eGk,536
|
|
2
2
|
oxutils/apps.py,sha256=8pO8eXUZeKYn8fPo0rkoytmHACwDNuTNhdRcpkPTxGM,347
|
|
3
3
|
oxutils/audit/__init__.py,sha256=uonc00G73Xm7RwRHVWD-wBn8lJYNCq3iBgnRGMWAEWs,583
|
|
4
4
|
oxutils/audit/apps.py,sha256=xvnmB5Z6nLV7ejzhSeQbesTkwRoFygoPFob8H5QTHgU,304
|
|
@@ -12,16 +12,17 @@ oxutils/celery/__init__.py,sha256=29jo4DfdvOoThX-jfL0ZiDjsy3-Z_fNhwHVJaLO5rsk,29
|
|
|
12
12
|
oxutils/celery/base.py,sha256=qLlBU2XvT2zj8qszy8togqH7hM_wUYyWWA3JAQPPJx0,3378
|
|
13
13
|
oxutils/celery/settings.py,sha256=njhHBErpcFczV2e23NCPX_Jxs015jr4dIig4Is_wbgE,33
|
|
14
14
|
oxutils/conf.py,sha256=JT0Zj4Wmn9HCEDpk7eOIY47YLBGP793tstsm34cLb1A,296
|
|
15
|
+
oxutils/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
oxutils/context/site_name_processor.py,sha256=1gc0Td_3HVlUn9ThhQBCQ8kfnRnI88bEflK9vEzTvEc,225
|
|
15
17
|
oxutils/enums/__init__.py,sha256=gFhZG8ER6ArGZO5agWhdfs7NiD2h9FzrzfQRHq18dD0,40
|
|
16
18
|
oxutils/enums/audit.py,sha256=ju2Z9CrtdyPziRQ7oOe4Ygw85t9sW3jynO_1DkgZoAM,126
|
|
17
19
|
oxutils/enums/invoices.py,sha256=E33QGQeutZUqvlovJY0VGDxWUb0i_kdfhEiir1ARKuQ,201
|
|
18
20
|
oxutils/exceptions.py,sha256=CCjENOD0of6_noif2ajrpfbBLoG16DWa46iB9_uEe3M,3592
|
|
19
|
-
oxutils/functions.py,sha256=
|
|
21
|
+
oxutils/functions.py,sha256=4stHj94VebWX0s1XeWshubMD2v8w8QztTWppbkTE_Gg,3246
|
|
20
22
|
oxutils/jwt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
23
|
oxutils/jwt/auth.py,sha256=rO-xWNfug9Ok6zA7EIPvVkpD8TBUdq05CdrnMrL-t9Q,1597
|
|
22
24
|
oxutils/jwt/client.py,sha256=bskLpmSBrehi_snbo3Qbq1m99Kbfg2GP7jqfcXKHvys,3341
|
|
23
25
|
oxutils/jwt/constants.py,sha256=MUahZjm8plTYpHjLOMQCuH0H18lkIwS45EtRm617wq8,26
|
|
24
|
-
oxutils/locale/fr/LC_MESSAGES/django.mo,sha256=Qk5R90E-JqJX2JxSnveKXxecKVHR-a76uFlwkHTF7f0,8114
|
|
25
26
|
oxutils/locale/fr/LC_MESSAGES/django.po,sha256=APXt_8R99seCWjJyS5ELOawvRLvUqqBT32O252BaG5s,7971
|
|
26
27
|
oxutils/logger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
28
|
oxutils/logger/receivers.py,sha256=U8JVjHRK1zUaCRnPn6p1qsm6FFXgTkXp67PpJ1LnjgU,542
|
|
@@ -37,9 +38,9 @@ oxutils/models/invoice.py,sha256=nqphkhlBhssODm2H4dBYyb1cOmHS29fToER40UN0cXo,132
|
|
|
37
38
|
oxutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
39
|
oxutils/s3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
40
|
oxutils/s3/settings.py,sha256=NIlVhaOzWdsepOgCpxdTTJRHfM0tM5EcAoy4kaFC1R8,1190
|
|
40
|
-
oxutils/s3/storages.py,sha256=
|
|
41
|
-
oxutils/settings.py,sha256=
|
|
41
|
+
oxutils/s3/storages.py,sha256=gjQg05edVn6NuyfJZ-NwUB2lRWwg8GqgzHB8I1D5vbI,5402
|
|
42
|
+
oxutils/settings.py,sha256=mp_ZSf8bcCUe2Zc9YdOe_EtltBgIY2n_8lUERQtsIFk,9742
|
|
42
43
|
oxutils/types.py,sha256=DIz8YK8xMpLc7FYbf88yEElyLsYN_-rbvaZXvENQkOQ,234
|
|
43
|
-
oxutils-0.1.
|
|
44
|
-
oxutils-0.1.
|
|
45
|
-
oxutils-0.1.
|
|
44
|
+
oxutils-0.1.3.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
45
|
+
oxutils-0.1.3.dist-info/METADATA,sha256=vv_16w64xVv_z5oBhs6s-hd4PggohmXePcPQh8U-RRw,6254
|
|
46
|
+
oxutils-0.1.3.dist-info/RECORD,,
|
|
Binary file
|