oxutils 0.1.0__tar.gz → 0.1.2__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 (45) hide show
  1. {oxutils-0.1.0 → oxutils-0.1.2}/PKG-INFO +14 -2
  2. {oxutils-0.1.0 → oxutils-0.1.2}/README.md +13 -1
  3. {oxutils-0.1.0 → oxutils-0.1.2}/pyproject.toml +1 -1
  4. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/__init__.py +1 -1
  5. oxutils-0.1.2/src/oxutils/audit/__init__.py +20 -0
  6. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/audit/apps.py +3 -3
  7. oxutils-0.1.2/src/oxutils/audit/migrations/0001_initial.py +41 -0
  8. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/conf.py +1 -0
  9. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/s3/storages.py +4 -4
  10. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/settings.py +13 -13
  11. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/apps.py +0 -0
  12. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/audit/export.py +0 -0
  13. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/audit/masks.py +0 -0
  14. {oxutils-0.1.0/src/oxutils/audit → oxutils-0.1.2/src/oxutils/audit/migrations}/__init__.py +0 -0
  15. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/audit/models.py +0 -0
  16. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/audit/settings.py +0 -0
  17. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/celery/__init__.py +0 -0
  18. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/celery/base.py +0 -0
  19. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/celery/settings.py +0 -0
  20. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/enums/__init__.py +0 -0
  21. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/enums/audit.py +0 -0
  22. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/enums/invoices.py +0 -0
  23. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/exceptions.py +0 -0
  24. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/functions.py +0 -0
  25. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/jwt/__init__.py +0 -0
  26. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/jwt/auth.py +0 -0
  27. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/jwt/client.py +0 -0
  28. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/jwt/constants.py +0 -0
  29. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/locale/fr/LC_MESSAGES/django.mo +0 -0
  30. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/locale/fr/LC_MESSAGES/django.po +0 -0
  31. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/logger/__init__.py +0 -0
  32. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/logger/receivers.py +0 -0
  33. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/logger/settings.py +0 -0
  34. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/mixins/__init__.py +0 -0
  35. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/mixins/base.py +0 -0
  36. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/mixins/schemas.py +0 -0
  37. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/mixins/services.py +0 -0
  38. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/models/__init__.py +0 -0
  39. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/models/base.py +0 -0
  40. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/models/billing.py +0 -0
  41. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/models/invoice.py +0 -0
  42. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/py.typed +0 -0
  43. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/s3/__init__.py +0 -0
  44. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/s3/settings.py +0 -0
  45. {oxutils-0.1.0 → oxutils-0.1.2}/src/oxutils/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oxutils
3
- Version: 0.1.0
3
+ Version: 0.1.2
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
@@ -153,6 +153,18 @@ uv sync
153
153
  uv run pytest # 126 tests
154
154
  ```
155
155
 
156
+ ### Creating Migrations
157
+
158
+ To generate Django migrations for the audit module:
159
+
160
+ ```bash
161
+ make migrations
162
+ # or
163
+ uv run make_migrations.py
164
+ ```
165
+
166
+ See [MIGRATIONS.md](MIGRATIONS.md) for detailed documentation.
167
+
156
168
  ## Advanced Examples
157
169
 
158
170
  ### JWT with Django Ninja
@@ -189,7 +201,7 @@ print(f"Exported to {export.data.url}")
189
201
 
190
202
  ## License
191
203
 
192
- MIT License - see [LICENSE](LICENSE)
204
+ Apache 2.0 License - see [LICENSE](LICENSE)
193
205
 
194
206
  ## Support
195
207
 
@@ -111,6 +111,18 @@ uv sync
111
111
  uv run pytest # 126 tests
112
112
  ```
113
113
 
114
+ ### Creating Migrations
115
+
116
+ To generate Django migrations for the audit module:
117
+
118
+ ```bash
119
+ make migrations
120
+ # or
121
+ uv run make_migrations.py
122
+ ```
123
+
124
+ See [MIGRATIONS.md](MIGRATIONS.md) for detailed documentation.
125
+
114
126
  ## Advanced Examples
115
127
 
116
128
  ### JWT with Django Ninja
@@ -147,7 +159,7 @@ print(f"Exported to {export.data.url}")
147
159
 
148
160
  ## License
149
161
 
150
- MIT License - see [LICENSE](LICENSE)
162
+ Apache 2.0 License - see [LICENSE](LICENSE)
151
163
 
152
164
  ## Support
153
165
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "oxutils"
3
- version = "0.1.0"
3
+ version = "0.1.2"
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.0"
13
+ __version__ = "0.1.1"
14
14
 
15
15
  from oxutils.settings import oxi_settings
16
16
  from oxutils.conf import UTILS_APPS, AUDIT_MIDDLEWARE
@@ -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}")
@@ -3,10 +3,10 @@ from django.utils.translation import gettext_lazy as _
3
3
 
4
4
 
5
5
 
6
- class OxutilsConfig(AppConfig):
6
+ class OxutilsAuditConfig(AppConfig):
7
7
  default_auto_field = 'django.db.models.BigAutoField'
8
- name = 'oxutils_export'
9
- verbose_name = _("Oxutils Export")
8
+ name = 'oxutils.audit'
9
+ verbose_name = _("Oxutils Audit")
10
10
 
11
11
  def ready(self):
12
12
  return super().ready()
@@ -0,0 +1,41 @@
1
+ # Generated by Django 5.2.8 on 2025-12-03 21:53
2
+
3
+ import django.db.models.deletion
4
+ import oxutils.enums.audit
5
+ import oxutils.s3.storages
6
+ from django.db import migrations, models
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+
11
+ initial = True
12
+
13
+ dependencies = [
14
+ ]
15
+
16
+ operations = [
17
+ migrations.CreateModel(
18
+ name='LogExportState',
19
+ fields=[
20
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
+ ('created_at', models.DateTimeField(auto_now_add=True, help_text='Date and time when this record was created')),
22
+ ('updated_at', models.DateTimeField(auto_now=True, help_text='Date and time when this record was last updated')),
23
+ ('last_export_date', models.DateTimeField(null=True)),
24
+ ('status', models.CharField(choices=[(oxutils.enums.audit.ExportStatus['FAILED'], 'Failed'), (oxutils.enums.audit.ExportStatus['PENDING'], 'Pending'), (oxutils.enums.audit.ExportStatus['SUCCESS'], 'Success')], default=oxutils.enums.audit.ExportStatus['PENDING'])),
25
+ ('data', models.FileField(storage=oxutils.s3.storages.LogStorage(), upload_to='')),
26
+ ('size', models.BigIntegerField()),
27
+ ],
28
+ options={
29
+ 'abstract': False,
30
+ },
31
+ ),
32
+ migrations.CreateModel(
33
+ name='LogExportHistory',
34
+ fields=[
35
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
36
+ ('status', models.CharField(choices=[(oxutils.enums.audit.ExportStatus['FAILED'], 'Failed'), (oxutils.enums.audit.ExportStatus['PENDING'], 'Pending'), (oxutils.enums.audit.ExportStatus['SUCCESS'], 'Success')], default=oxutils.enums.audit.ExportStatus['PENDING'])),
37
+ ('created_at', models.DateTimeField(auto_now_add=True, help_text='Date and time when this record was created')),
38
+ ('state', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='log_histories', to='audit.logexportstate')),
39
+ ],
40
+ ),
41
+ ]
@@ -3,6 +3,7 @@ UTILS_APPS = (
3
3
  'auditlog',
4
4
  'cid.apps.CidAppConfig',
5
5
  'django_celery_results',
6
+ 'oxutils.audit',
6
7
  )
7
8
 
8
9
  AUDIT_MIDDLEWARE = (
@@ -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
@@ -52,7 +52,7 @@ class OxUtilsSettings(BaseSettings):
52
52
  default_s3_secret_access_key: Optional[str] = None
53
53
  default_s3_storage_bucket_name: Optional[str] = None
54
54
  default_s3_default_acl: str = Field('public-read')
55
- default_s3_s3_custom_domain: Optional[str] = None
55
+ default_s3_custom_domain: Optional[str] = None
56
56
  default_s3_location: str = Field('media')
57
57
  default_s3_storage: str = Field('oxutils.s3.storages.PublicMediaStorage')
58
58
 
@@ -62,7 +62,7 @@ class OxUtilsSettings(BaseSettings):
62
62
  private_s3_secret_access_key: Optional[str] = None
63
63
  private_s3_storage_bucket_name: Optional[str] = None
64
64
  private_s3_default_acl: str = Field('private')
65
- private_s3_s3_custom_domain: Optional[str] = None
65
+ private_s3_custom_domain: Optional[str] = None
66
66
  private_s3_location: str = Field('private')
67
67
  private_s3_storage: str = Field('oxutils.s3.storages.PrivateMediaStorage')
68
68
 
@@ -73,7 +73,7 @@ class OxUtilsSettings(BaseSettings):
73
73
  log_s3_secret_access_key: Optional[str] = None
74
74
  log_s3_storage_bucket_name: Optional[str] = None
75
75
  log_s3_default_acl: str = Field('private')
76
- log_s3_s3_custom_domain: Optional[str] = None
76
+ log_s3_custom_domain: Optional[str] = None
77
77
  log_s3_location: str = Field('oxi_logs')
78
78
  log_s3_storage: str = Field('oxutils.s3.storages.LogStorage')
79
79
 
@@ -102,7 +102,7 @@ class OxUtilsSettings(BaseSettings):
102
102
  self.default_s3_access_key_id,
103
103
  self.default_s3_secret_access_key,
104
104
  self.default_s3_storage_bucket_name,
105
- self.default_s3_s3_custom_domain
105
+ self.default_s3_custom_domain
106
106
  )
107
107
  elif not self.use_static_s3:
108
108
  raise ValueError(
@@ -116,7 +116,7 @@ class OxUtilsSettings(BaseSettings):
116
116
  self.private_s3_access_key_id,
117
117
  self.private_s3_secret_access_key,
118
118
  self.private_s3_storage_bucket_name,
119
- self.private_s3_s3_custom_domain
119
+ self.private_s3_custom_domain
120
120
  )
121
121
 
122
122
  # Validate log S3
@@ -127,7 +127,7 @@ class OxUtilsSettings(BaseSettings):
127
127
  self.log_s3_access_key_id,
128
128
  self.log_s3_secret_access_key,
129
129
  self.log_s3_storage_bucket_name,
130
- self.log_s3_s3_custom_domain
130
+ self.log_s3_custom_domain
131
131
  )
132
132
  elif not self.use_private_s3:
133
133
  raise ValueError(
@@ -166,11 +166,11 @@ class OxUtilsSettings(BaseSettings):
166
166
  """Validate required S3 configuration fields."""
167
167
  missing_fields = []
168
168
  if not access_key:
169
- missing_fields.append(f'OXI_{name.upper()}_ACCESS_KEY_ID')
169
+ missing_fields.append(f'OXI_{name.upper()}_S3_ACCESS_KEY_ID')
170
170
  if not secret_key:
171
- missing_fields.append(f'OXI_{name.upper()}_SECRET_ACCESS_KEY')
171
+ missing_fields.append(f'OXI_{name.upper()}_S3_SECRET_ACCESS_KEY')
172
172
  if not bucket:
173
- missing_fields.append(f'OXI_{name.upper()}_STORAGE_BUCKET_NAME')
173
+ missing_fields.append(f'OXI_{name.upper()}_S3_STORAGE_BUCKET_NAME')
174
174
  if not domain:
175
175
  missing_fields.append(f'OXI_{name.upper()}_S3_CUSTOM_DOMAIN')
176
176
 
@@ -194,7 +194,7 @@ class OxUtilsSettings(BaseSettings):
194
194
  # Use static S3 credentials but keep default_s3 specific values (location, etc.)
195
195
  domain = self.static_s3_custom_domain
196
196
  else:
197
- domain = self.default_s3_s3_custom_domain
197
+ domain = self.default_s3_custom_domain
198
198
  return f'https://{domain}/{self.default_s3_location}/'
199
199
 
200
200
  raise ImproperlyConfigured(
@@ -207,7 +207,7 @@ class OxUtilsSettings(BaseSettings):
207
207
  raise ImproperlyConfigured(
208
208
  "Private S3 is not enabled. Set OXI_USE_PRIVATE_S3=True."
209
209
  )
210
- return f'https://{self.private_s3_s3_custom_domain}/{self.private_s3_location}/'
210
+ return f'https://{self.private_s3_custom_domain}/{self.private_s3_location}/'
211
211
 
212
212
  def get_log_storage_url(self) -> str:
213
213
  """Get log storage URL."""
@@ -217,9 +217,9 @@ class OxUtilsSettings(BaseSettings):
217
217
  )
218
218
  if self.use_private_s3_as_log:
219
219
  # Use private S3 credentials but keep log_s3 specific values (location, etc.)
220
- domain = self.private_s3_s3_custom_domain
220
+ domain = self.private_s3_custom_domain
221
221
  else:
222
- domain = self.log_s3_s3_custom_domain
222
+ domain = self.log_s3_custom_domain
223
223
  return f'https://{domain}/{self.log_s3_location}/{self.service_name}/'
224
224
 
225
225
 
File without changes
File without changes
File without changes
File without changes