oxutils 0.1.1__tar.gz → 0.1.3__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 (47) hide show
  1. {oxutils-0.1.1 → oxutils-0.1.3}/PKG-INFO +15 -3
  2. {oxutils-0.1.1 → oxutils-0.1.3}/README.md +14 -2
  3. {oxutils-0.1.1 → oxutils-0.1.3}/pyproject.toml +1 -1
  4. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/__init__.py +1 -1
  5. oxutils-0.1.3/src/oxutils/context/site_name_processor.py +11 -0
  6. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/functions.py +5 -2
  7. oxutils-0.1.3/src/oxutils/s3/__init__.py +0 -0
  8. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/s3/storages.py +4 -4
  9. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/settings.py +15 -13
  10. oxutils-0.1.1/src/oxutils/locale/fr/LC_MESSAGES/django.mo +0 -0
  11. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/apps.py +0 -0
  12. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/audit/__init__.py +0 -0
  13. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/audit/apps.py +0 -0
  14. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/audit/export.py +0 -0
  15. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/audit/masks.py +0 -0
  16. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/audit/migrations/0001_initial.py +0 -0
  17. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/audit/migrations/__init__.py +0 -0
  18. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/audit/models.py +0 -0
  19. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/audit/settings.py +0 -0
  20. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/celery/__init__.py +0 -0
  21. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/celery/base.py +0 -0
  22. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/celery/settings.py +0 -0
  23. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/conf.py +0 -0
  24. {oxutils-0.1.1/src/oxutils/jwt → oxutils-0.1.3/src/oxutils/context}/__init__.py +0 -0
  25. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/enums/__init__.py +0 -0
  26. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/enums/audit.py +0 -0
  27. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/enums/invoices.py +0 -0
  28. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/exceptions.py +0 -0
  29. {oxutils-0.1.1/src/oxutils/logger → oxutils-0.1.3/src/oxutils/jwt}/__init__.py +0 -0
  30. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/jwt/auth.py +0 -0
  31. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/jwt/client.py +0 -0
  32. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/jwt/constants.py +0 -0
  33. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/locale/fr/LC_MESSAGES/django.po +0 -0
  34. {oxutils-0.1.1/src/oxutils/mixins → oxutils-0.1.3/src/oxutils/logger}/__init__.py +0 -0
  35. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/logger/receivers.py +0 -0
  36. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/logger/settings.py +0 -0
  37. {oxutils-0.1.1/src/oxutils/s3 → oxutils-0.1.3/src/oxutils/mixins}/__init__.py +0 -0
  38. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/mixins/base.py +0 -0
  39. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/mixins/schemas.py +0 -0
  40. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/mixins/services.py +0 -0
  41. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/models/__init__.py +0 -0
  42. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/models/base.py +0 -0
  43. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/models/billing.py +0 -0
  44. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/models/invoice.py +0 -0
  45. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/py.typed +0 -0
  46. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/s3/settings.py +0 -0
  47. {oxutils-0.1.1 → oxutils-0.1.3}/src/oxutils/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oxutils
3
- Version: 0.1.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
  [![PyPI version](https://img.shields.io/pypi/v/oxutils.svg)](https://pypi.org/project/oxutils/)
48
48
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/)
49
49
  [![Django 5.0+](https://img.shields.io/badge/django-5.0+-green.svg)](https://www.djangoproject.com/)
50
- [![Tests](https://img.shields.io/badge/tests-126%20passed-success.svg)](tests/)
50
+ [![Tests](https://img.shields.io/badge/tests-145%20passed-success.svg)](tests/)
51
51
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
52
52
  [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](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 # 126 tests
165
+ uv run pytest # 145 tests
154
166
  ```
155
167
 
156
168
  ### Creating Migrations
@@ -5,7 +5,7 @@
5
5
  [![PyPI version](https://img.shields.io/pypi/v/oxutils.svg)](https://pypi.org/project/oxutils/)
6
6
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/)
7
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-126%20passed-success.svg)](tests/)
8
+ [![Tests](https://img.shields.io/badge/tests-145%20passed-success.svg)](tests/)
9
9
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
10
10
  [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
11
11
 
@@ -18,6 +18,7 @@
18
18
  - ⚙️ **Celery Integration** - Pre-configured task processing
19
19
  - 🛠️ **Django Mixins** - UUID, timestamps, user tracking
20
20
  - ⚡ **Custom Exceptions** - Standardized API errors
21
+ - 🎨 **Context Processors** - Site name and domain for templates
21
22
 
22
23
  ---
23
24
 
@@ -84,6 +85,17 @@ class Product(BaseModelMixin): # UUID + timestamps + is_active
84
85
  # Custom Exceptions
85
86
  from oxutils.exceptions import NotFoundException
86
87
  raise NotFoundException(detail="User not found")
88
+
89
+ # Context Processors
90
+ # settings.py
91
+ TEMPLATES = [{
92
+ 'OPTIONS': {
93
+ 'context_processors': [
94
+ 'oxutils.context.site_name_processor.site_name',
95
+ ],
96
+ },
97
+ }]
98
+ # Now {{ site_name }} and {{ site_domain }} are available in templates
87
99
  ```
88
100
 
89
101
  ## Documentation
@@ -108,7 +120,7 @@ raise NotFoundException(detail="User not found")
108
120
  git clone https://github.com/oxiliere/oxutils.git
109
121
  cd oxutils
110
122
  uv sync
111
- uv run pytest # 126 tests
123
+ uv run pytest # 145 tests
112
124
  ```
113
125
 
114
126
  ### Creating Migrations
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "oxutils"
3
- version = "0.1.1"
3
+ version = "0.1.3"
4
4
  description = "Production-ready utilities for Django applications in the Oxiliere ecosystem"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -10,7 +10,7 @@ This package provides:
10
10
  - Custom exceptions
11
11
  """
12
12
 
13
- __version__ = "0.1.1"
13
+ __version__ = "0.1.3"
14
14
 
15
15
  from oxutils.settings import oxi_settings
16
16
  from oxutils.conf import UTILS_APPS, AUDIT_MIDDLEWARE
@@ -0,0 +1,11 @@
1
+ """
2
+ Context processor for site name
3
+ """
4
+
5
+ def site_name(request):
6
+ from oxutils.settings import oxi_settings
7
+
8
+ return {
9
+ 'site_name': oxi_settings.site_name,
10
+ 'site_domain': oxi_settings.site_domain,
11
+ }
@@ -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 MEDIA_URL and domain
14
- base_url = getattr(settings, 'SITE_URL', 'http://localhost:8000')
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
 
File without changes
@@ -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.default_s3_s3_custom_domain
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.private_s3_s3_custom_domain
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.private_s3_s3_custom_domain
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.log_s3_s3_custom_domain
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
@@ -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
- default_s3_s3_custom_domain: Optional[str] = None
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
- private_s3_s3_custom_domain: Optional[str] = None
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
- log_s3_s3_custom_domain: Optional[str] = None
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.default_s3_s3_custom_domain
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.private_s3_s3_custom_domain
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.log_s3_s3_custom_domain
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()}_ACCESS_KEY_ID')
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()}_SECRET_ACCESS_KEY')
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()}_STORAGE_BUCKET_NAME')
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.default_s3_s3_custom_domain
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.private_s3_s3_custom_domain}/{self.private_s3_location}/'
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.private_s3_s3_custom_domain
222
+ domain = self.private_s3_custom_domain
221
223
  else:
222
- domain = self.log_s3_s3_custom_domain
224
+ domain = self.log_s3_custom_domain
223
225
  return f'https://{domain}/{self.log_s3_location}/{self.service_name}/'
224
226
 
225
227
 
File without changes
File without changes
File without changes
File without changes
File without changes