django-nativemojo 0.1.10__py3-none-any.whl → 0.1.15__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.
Files changed (120) hide show
  1. django_nativemojo-0.1.15.dist-info/METADATA +136 -0
  2. {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.15.dist-info}/RECORD +105 -65
  3. mojo/__init__.py +1 -1
  4. mojo/apps/account/management/__init__.py +5 -0
  5. mojo/apps/account/management/commands/__init__.py +6 -0
  6. mojo/apps/account/management/commands/serializer_admin.py +531 -0
  7. mojo/apps/account/migrations/0004_user_avatar.py +20 -0
  8. mojo/apps/account/migrations/0005_group_last_activity.py +18 -0
  9. mojo/apps/account/models/group.py +25 -7
  10. mojo/apps/account/models/member.py +15 -4
  11. mojo/apps/account/models/user.py +197 -20
  12. mojo/apps/account/rest/group.py +1 -0
  13. mojo/apps/account/rest/user.py +6 -2
  14. mojo/apps/aws/rest/__init__.py +1 -0
  15. mojo/apps/aws/rest/s3.py +64 -0
  16. mojo/apps/fileman/README.md +8 -8
  17. mojo/apps/fileman/backends/base.py +76 -70
  18. mojo/apps/fileman/backends/filesystem.py +86 -86
  19. mojo/apps/fileman/backends/s3.py +200 -108
  20. mojo/apps/fileman/migrations/0001_initial.py +106 -0
  21. mojo/apps/fileman/migrations/0002_filemanager_parent_alter_filemanager_max_file_size.py +24 -0
  22. mojo/apps/fileman/migrations/0003_remove_file_fileman_fil_upload__c4bc35_idx_and_more.py +25 -0
  23. mojo/apps/fileman/migrations/0004_remove_file_original_filename_and_more.py +39 -0
  24. mojo/apps/fileman/migrations/0005_alter_file_upload_token.py +18 -0
  25. mojo/apps/fileman/migrations/0006_file_download_url_filemanager_forever_urls.py +23 -0
  26. mojo/apps/fileman/migrations/0007_remove_filemanager_forever_urls_and_more.py +22 -0
  27. mojo/apps/fileman/migrations/0008_file_category.py +18 -0
  28. mojo/apps/fileman/migrations/0009_rename_file_path_file_storage_file_path.py +18 -0
  29. mojo/apps/fileman/migrations/0010_filerendition.py +33 -0
  30. mojo/apps/fileman/migrations/0011_alter_filerendition_original_file.py +19 -0
  31. mojo/apps/fileman/models/__init__.py +1 -5
  32. mojo/apps/fileman/models/file.py +204 -58
  33. mojo/apps/fileman/models/manager.py +161 -31
  34. mojo/apps/fileman/models/rendition.py +118 -0
  35. mojo/apps/fileman/renderer/__init__.py +111 -0
  36. mojo/apps/fileman/renderer/audio.py +403 -0
  37. mojo/apps/fileman/renderer/base.py +205 -0
  38. mojo/apps/fileman/renderer/document.py +404 -0
  39. mojo/apps/fileman/renderer/image.py +222 -0
  40. mojo/apps/fileman/renderer/utils.py +297 -0
  41. mojo/apps/fileman/renderer/video.py +304 -0
  42. mojo/apps/fileman/rest/__init__.py +1 -18
  43. mojo/apps/fileman/rest/upload.py +22 -32
  44. mojo/apps/fileman/signals.py +58 -0
  45. mojo/apps/fileman/tasks.py +254 -0
  46. mojo/apps/fileman/utils/__init__.py +40 -16
  47. mojo/apps/incident/migrations/0005_incidenthistory.py +39 -0
  48. mojo/apps/incident/migrations/0006_alter_incident_state.py +18 -0
  49. mojo/apps/incident/models/__init__.py +1 -0
  50. mojo/apps/incident/models/history.py +36 -0
  51. mojo/apps/incident/models/incident.py +1 -1
  52. mojo/apps/incident/reporter.py +3 -1
  53. mojo/apps/incident/rest/event.py +7 -1
  54. mojo/apps/logit/migrations/0004_alter_log_level.py +18 -0
  55. mojo/apps/logit/models/log.py +4 -1
  56. mojo/apps/metrics/utils.py +2 -2
  57. mojo/apps/notify/handlers/ses/message.py +1 -1
  58. mojo/apps/notify/providers/aws.py +2 -2
  59. mojo/apps/tasks/__init__.py +34 -1
  60. mojo/apps/tasks/manager.py +200 -45
  61. mojo/apps/tasks/rest/tasks.py +24 -10
  62. mojo/apps/tasks/runner.py +283 -18
  63. mojo/apps/tasks/task.py +99 -0
  64. mojo/apps/tasks/tq_handlers.py +118 -0
  65. mojo/decorators/auth.py +6 -1
  66. mojo/decorators/http.py +7 -2
  67. mojo/helpers/aws/__init__.py +41 -0
  68. mojo/helpers/aws/ec2.py +804 -0
  69. mojo/helpers/aws/iam.py +748 -0
  70. mojo/helpers/aws/s3.py +451 -11
  71. mojo/helpers/aws/ses.py +483 -0
  72. mojo/helpers/aws/sns.py +461 -0
  73. mojo/helpers/crypto/__pycache__/hash.cpython-310.pyc +0 -0
  74. mojo/helpers/crypto/__pycache__/sign.cpython-310.pyc +0 -0
  75. mojo/helpers/crypto/__pycache__/utils.cpython-310.pyc +0 -0
  76. mojo/helpers/dates.py +18 -0
  77. mojo/helpers/response.py +6 -2
  78. mojo/helpers/settings/__init__.py +2 -0
  79. mojo/helpers/{settings.py → settings/helper.py} +1 -37
  80. mojo/helpers/settings/parser.py +132 -0
  81. mojo/middleware/logging.py +1 -1
  82. mojo/middleware/mojo.py +5 -0
  83. mojo/models/rest.py +261 -46
  84. mojo/models/secrets.py +13 -4
  85. mojo/serializers/__init__.py +100 -0
  86. mojo/serializers/advanced/README.md +363 -0
  87. mojo/serializers/advanced/__init__.py +247 -0
  88. mojo/serializers/advanced/formats/__init__.py +28 -0
  89. mojo/serializers/advanced/formats/csv.py +416 -0
  90. mojo/serializers/advanced/formats/excel.py +516 -0
  91. mojo/serializers/advanced/formats/json.py +239 -0
  92. mojo/serializers/advanced/formats/localizers.py +509 -0
  93. mojo/serializers/advanced/formats/response.py +485 -0
  94. mojo/serializers/advanced/serializer.py +568 -0
  95. mojo/serializers/manager.py +501 -0
  96. mojo/serializers/optimized.py +618 -0
  97. mojo/serializers/settings_example.py +322 -0
  98. mojo/serializers/{models.py → simple.py} +38 -15
  99. testit/helpers.py +21 -4
  100. django_nativemojo-0.1.10.dist-info/METADATA +0 -96
  101. mojo/apps/metrics/rest/db.py +0 -0
  102. mojo/helpers/aws/setup_email.py +0 -0
  103. mojo/ws4redis/README.md +0 -174
  104. mojo/ws4redis/__init__.py +0 -2
  105. mojo/ws4redis/client.py +0 -283
  106. mojo/ws4redis/connection.py +0 -327
  107. mojo/ws4redis/exceptions.py +0 -32
  108. mojo/ws4redis/redis.py +0 -183
  109. mojo/ws4redis/servers/base.py +0 -86
  110. mojo/ws4redis/servers/django.py +0 -171
  111. mojo/ws4redis/servers/uwsgi.py +0 -63
  112. mojo/ws4redis/settings.py +0 -45
  113. mojo/ws4redis/utf8validator.py +0 -128
  114. mojo/ws4redis/websocket.py +0 -403
  115. {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.15.dist-info}/LICENSE +0 -0
  116. {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.15.dist-info}/NOTICE +0 -0
  117. {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.15.dist-info}/WHEEL +0 -0
  118. /mojo/{ws4redis/servers → apps/aws}/__init__.py +0 -0
  119. /mojo/apps/{fileman/models/render.py → aws/models/__init__.py} +0 -0
  120. /mojo/apps/fileman/{rest/__init__ → migrations/__init__.py} +0 -0
@@ -8,101 +8,101 @@ class StorageBackend(ABC):
8
8
  """
9
9
  Abstract base class for all storage backends
10
10
  """
11
-
11
+
12
12
  def __init__(self, file_manager, **kwargs):
13
13
  """
14
14
  Initialize the storage backend with a FileManager instance
15
-
15
+
16
16
  Args:
17
17
  file_manager: FileManager instance with configuration
18
18
  **kwargs: Additional backend-specific configuration
19
19
  """
20
20
  self.file_manager = file_manager
21
- self.settings = file_manager.settings
21
+ self.settings = file_manager.primary_settings
22
22
  self.backend_url = file_manager.backend_url
23
23
  self.config = kwargs
24
-
24
+
25
25
  @abstractmethod
26
- def save(self, file_obj, filename: str, **kwargs) -> str:
26
+ def save(self, file_obj, file_path: str, content_type: Optional[str] = None, metadata: Optional[dict] = None) -> str:
27
27
  """
28
28
  Save a file to the storage backend
29
-
29
+
30
30
  Args:
31
31
  file_obj: File-like object to save
32
32
  filename: Name to save the file as
33
33
  **kwargs: Additional save options
34
-
34
+
35
35
  Returns:
36
36
  str: Full path to the saved file
37
37
  """
38
38
  pass
39
-
39
+
40
40
  @abstractmethod
41
41
  def delete(self, file_path: str) -> bool:
42
42
  """
43
43
  Delete a file from the storage backend
44
-
44
+
45
45
  Args:
46
46
  file_path: Path to the file to delete
47
-
47
+
48
48
  Returns:
49
49
  bool: True if deletion was successful
50
50
  """
51
51
  pass
52
-
52
+
53
53
  @abstractmethod
54
54
  def exists(self, file_path: str) -> bool:
55
55
  """
56
56
  Check if a file exists in the storage backend
57
-
57
+
58
58
  Args:
59
59
  file_path: Path to check
60
-
60
+
61
61
  Returns:
62
62
  bool: True if file exists
63
63
  """
64
64
  pass
65
-
65
+
66
66
  @abstractmethod
67
67
  def get_file_size(self, file_path: str) -> Optional[int]:
68
68
  """
69
69
  Get the size of a file in bytes
70
-
70
+
71
71
  Args:
72
72
  file_path: Path to the file
73
-
73
+
74
74
  Returns:
75
75
  Optional[int]: File size in bytes, None if file doesn't exist
76
76
  """
77
77
  pass
78
-
78
+
79
79
  @abstractmethod
80
80
  def get_url(self, file_path: str, expires_in: Optional[int] = None) -> str:
81
81
  """
82
82
  Get a URL to access the file
83
-
83
+
84
84
  Args:
85
85
  file_path: Path to the file
86
86
  expires_in: Optional expiration time in seconds
87
-
87
+
88
88
  Returns:
89
89
  str: URL to access the file
90
90
  """
91
91
  pass
92
-
92
+
93
93
  @abstractmethod
94
- def generate_upload_url(self, file_path: str, content_type: str,
95
- file_size: Optional[int] = None,
94
+ def generate_upload_url(self, file_path: str, content_type: str,
95
+ file_size: Optional[int] = None,
96
96
  expires_in: int = 3600) -> Dict[str, Any]:
97
97
  """
98
98
  Generate a pre-signed URL for direct upload
99
-
99
+
100
100
  Args:
101
101
  file_path: Path where the file will be stored
102
102
  content_type: MIME type of the file
103
103
  file_size: Expected file size in bytes
104
104
  expires_in: URL expiration time in seconds
105
-
105
+
106
106
  Returns:
107
107
  Dict containing:
108
108
  - upload_url: Pre-signed upload URL
@@ -111,54 +111,54 @@ class StorageBackend(ABC):
111
111
  - headers: Required headers (if any)
112
112
  """
113
113
  pass
114
-
114
+
115
115
  def supports_direct_upload(self) -> bool:
116
116
  """
117
117
  Check if this backend supports direct uploads
118
-
118
+
119
119
  Returns:
120
120
  bool: True if direct uploads are supported
121
121
  """
122
122
  return self.file_manager.supports_direct_upload
123
-
124
- def validate_upload(self, file_path: str, upload_token: str,
123
+
124
+ def validate_upload(self, file_path: str, upload_token: Optional[str] = None,
125
125
  expected_size: Optional[int] = None,
126
126
  expected_checksum: Optional[str] = None) -> Tuple[bool, str]:
127
127
  """
128
128
  Validate that an uploaded file matches expectations
129
-
129
+
130
130
  Args:
131
131
  file_path: Path to the uploaded file
132
132
  upload_token: Token used for the upload
133
133
  expected_size: Expected file size
134
134
  expected_checksum: Expected file checksum
135
-
135
+
136
136
  Returns:
137
137
  Tuple[bool, str]: (is_valid, error_message)
138
138
  """
139
139
  if not self.exists(file_path):
140
140
  return False, "File does not exist after upload"
141
-
141
+
142
142
  if expected_size:
143
143
  actual_size = self.get_file_size(file_path)
144
144
  if actual_size != expected_size:
145
145
  return False, f"File size mismatch: expected {expected_size}, got {actual_size}"
146
-
146
+
147
147
  if expected_checksum:
148
148
  actual_checksum = self.get_file_checksum(file_path)
149
149
  if actual_checksum != expected_checksum:
150
150
  return False, f"Checksum mismatch: expected {expected_checksum}, got {actual_checksum}"
151
-
151
+
152
152
  return True, "Upload validation successful"
153
-
153
+
154
154
  def get_file_checksum(self, file_path: str, algorithm: str = 'md5') -> Optional[str]:
155
155
  """
156
156
  Calculate checksum of a file
157
-
157
+
158
158
  Args:
159
159
  file_path: Path to the file
160
160
  algorithm: Hash algorithm to use (md5, sha256, etc.)
161
-
161
+
162
162
  Returns:
163
163
  Optional[str]: File checksum, None if calculation fails
164
164
  """
@@ -166,45 +166,45 @@ class StorageBackend(ABC):
166
166
  try:
167
167
  import hashlib
168
168
  hash_obj = hashlib.new(algorithm)
169
-
169
+
170
170
  with self.open(file_path, 'rb') as f:
171
171
  for chunk in iter(lambda: f.read(4096), b""):
172
172
  hash_obj.update(chunk)
173
-
173
+
174
174
  return hash_obj.hexdigest()
175
175
  except Exception:
176
176
  return None
177
-
177
+
178
178
  def open(self, file_path: str, mode: str = 'rb'):
179
179
  """
180
180
  Open a file from the storage backend
181
-
181
+
182
182
  Args:
183
183
  file_path: Path to the file
184
184
  mode: File open mode
185
-
185
+
186
186
  Returns:
187
187
  File-like object
188
188
  """
189
189
  raise NotImplementedError("Backend does not support file opening")
190
-
190
+
191
191
  def generate_file_path(self, filename: str, group_id: Optional[int] = None) -> str:
192
192
  """
193
193
  Generate a storage path for a file
194
-
194
+
195
195
  Args:
196
196
  filename: Original filename
197
197
  group_id: Optional group ID for organization
198
-
198
+
199
199
  Returns:
200
200
  str: Generated file path
201
201
  """
202
202
  # Default implementation - backends can override
203
203
  parts = []
204
-
204
+
205
205
  if group_id:
206
206
  parts.append(f"group_{group_id}")
207
-
207
+
208
208
  # Add date-based organization
209
209
  now = datetime.now()
210
210
  parts.extend([
@@ -212,58 +212,58 @@ class StorageBackend(ABC):
212
212
  f"{now.month:02d}",
213
213
  f"{now.day:02d}"
214
214
  ])
215
-
215
+
216
216
  parts.append(filename)
217
217
  return "/".join(parts)
218
-
218
+
219
219
  def get_available_space(self) -> Optional[int]:
220
220
  """
221
221
  Get available storage space in bytes
222
-
222
+
223
223
  Returns:
224
224
  Optional[int]: Available space in bytes, None if unlimited/unknown
225
225
  """
226
226
  return None
227
-
227
+
228
228
  def cleanup_expired_uploads(self, before_date: Optional[datetime] = None):
229
229
  """
230
230
  Clean up expired upload URLs and temporary files
231
-
231
+
232
232
  Args:
233
233
  before_date: Clean up uploads before this date (default: now)
234
234
  """
235
235
  # Default implementation does nothing - backends can override
236
236
  pass
237
-
237
+
238
238
  def get_file_metadata(self, file_path: str) -> Dict[str, Any]:
239
239
  """
240
240
  Get metadata for a file
241
-
241
+
242
242
  Args:
243
243
  file_path: Path to the file
244
-
244
+
245
245
  Returns:
246
246
  Dict containing file metadata
247
247
  """
248
248
  metadata = {}
249
-
249
+
250
250
  if self.exists(file_path):
251
251
  metadata['exists'] = True
252
252
  metadata['size'] = self.get_file_size(file_path)
253
253
  metadata['path'] = file_path
254
254
  else:
255
255
  metadata['exists'] = False
256
-
256
+
257
257
  return metadata
258
-
258
+
259
259
  def copy_file(self, source_path: str, dest_path: str) -> bool:
260
260
  """
261
261
  Copy a file within the storage backend
262
-
262
+
263
263
  Args:
264
264
  source_path: Source file path
265
265
  dest_path: Destination file path
266
-
266
+
267
267
  Returns:
268
268
  bool: True if copy was successful
269
269
  """
@@ -272,48 +272,54 @@ class StorageBackend(ABC):
272
272
  return self.save(source, dest_path) is not None
273
273
  except Exception:
274
274
  return False
275
-
275
+
276
276
  def move_file(self, source_path: str, dest_path: str) -> bool:
277
277
  """
278
278
  Move a file within the storage backend
279
-
279
+
280
280
  Args:
281
281
  source_path: Source file path
282
282
  dest_path: Destination file path
283
-
283
+
284
284
  Returns:
285
285
  bool: True if move was successful
286
286
  """
287
287
  if self.copy_file(source_path, dest_path):
288
288
  return self.delete(source_path)
289
289
  return False
290
-
290
+
291
291
  def list_files(self, path_prefix: str = "", limit: int = 1000) -> List[str]:
292
292
  """
293
293
  List files with optional path prefix
294
-
294
+
295
295
  Args:
296
296
  path_prefix: Optional path prefix to filter by
297
297
  limit: Maximum number of files to return
298
-
298
+
299
299
  Returns:
300
300
  List[str]: List of file paths
301
301
  """
302
302
  # Default implementation returns empty list - backends should override
303
303
  return []
304
-
304
+
305
305
  def get_setting(self, key: str, default: Any = None) -> Any:
306
306
  """
307
307
  Get a setting value from the file manager configuration
308
-
308
+
309
309
  Args:
310
310
  key: Setting key
311
311
  default: Default value if key not found
312
-
312
+
313
313
  Returns:
314
314
  Setting value
315
315
  """
316
316
  return self.settings.get(key, default)
317
-
317
+
318
+ def make_path_public(self):
319
+ return
320
+
321
+ def make_path_private(self):
322
+ return
323
+
318
324
  def __str__(self):
319
- return f"{self.__class__.__name__}({self.backend_url})"
325
+ return f"{self.__class__.__name__}({self.backend_url})"