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.
- django_nativemojo-0.1.15.dist-info/METADATA +136 -0
- {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.15.dist-info}/RECORD +105 -65
- mojo/__init__.py +1 -1
- mojo/apps/account/management/__init__.py +5 -0
- mojo/apps/account/management/commands/__init__.py +6 -0
- mojo/apps/account/management/commands/serializer_admin.py +531 -0
- mojo/apps/account/migrations/0004_user_avatar.py +20 -0
- mojo/apps/account/migrations/0005_group_last_activity.py +18 -0
- mojo/apps/account/models/group.py +25 -7
- mojo/apps/account/models/member.py +15 -4
- mojo/apps/account/models/user.py +197 -20
- mojo/apps/account/rest/group.py +1 -0
- mojo/apps/account/rest/user.py +6 -2
- mojo/apps/aws/rest/__init__.py +1 -0
- mojo/apps/aws/rest/s3.py +64 -0
- mojo/apps/fileman/README.md +8 -8
- mojo/apps/fileman/backends/base.py +76 -70
- mojo/apps/fileman/backends/filesystem.py +86 -86
- mojo/apps/fileman/backends/s3.py +200 -108
- mojo/apps/fileman/migrations/0001_initial.py +106 -0
- mojo/apps/fileman/migrations/0002_filemanager_parent_alter_filemanager_max_file_size.py +24 -0
- mojo/apps/fileman/migrations/0003_remove_file_fileman_fil_upload__c4bc35_idx_and_more.py +25 -0
- mojo/apps/fileman/migrations/0004_remove_file_original_filename_and_more.py +39 -0
- mojo/apps/fileman/migrations/0005_alter_file_upload_token.py +18 -0
- mojo/apps/fileman/migrations/0006_file_download_url_filemanager_forever_urls.py +23 -0
- mojo/apps/fileman/migrations/0007_remove_filemanager_forever_urls_and_more.py +22 -0
- mojo/apps/fileman/migrations/0008_file_category.py +18 -0
- mojo/apps/fileman/migrations/0009_rename_file_path_file_storage_file_path.py +18 -0
- mojo/apps/fileman/migrations/0010_filerendition.py +33 -0
- mojo/apps/fileman/migrations/0011_alter_filerendition_original_file.py +19 -0
- mojo/apps/fileman/models/__init__.py +1 -5
- mojo/apps/fileman/models/file.py +204 -58
- mojo/apps/fileman/models/manager.py +161 -31
- mojo/apps/fileman/models/rendition.py +118 -0
- mojo/apps/fileman/renderer/__init__.py +111 -0
- mojo/apps/fileman/renderer/audio.py +403 -0
- mojo/apps/fileman/renderer/base.py +205 -0
- mojo/apps/fileman/renderer/document.py +404 -0
- mojo/apps/fileman/renderer/image.py +222 -0
- mojo/apps/fileman/renderer/utils.py +297 -0
- mojo/apps/fileman/renderer/video.py +304 -0
- mojo/apps/fileman/rest/__init__.py +1 -18
- mojo/apps/fileman/rest/upload.py +22 -32
- mojo/apps/fileman/signals.py +58 -0
- mojo/apps/fileman/tasks.py +254 -0
- mojo/apps/fileman/utils/__init__.py +40 -16
- mojo/apps/incident/migrations/0005_incidenthistory.py +39 -0
- mojo/apps/incident/migrations/0006_alter_incident_state.py +18 -0
- mojo/apps/incident/models/__init__.py +1 -0
- mojo/apps/incident/models/history.py +36 -0
- mojo/apps/incident/models/incident.py +1 -1
- mojo/apps/incident/reporter.py +3 -1
- mojo/apps/incident/rest/event.py +7 -1
- mojo/apps/logit/migrations/0004_alter_log_level.py +18 -0
- mojo/apps/logit/models/log.py +4 -1
- mojo/apps/metrics/utils.py +2 -2
- mojo/apps/notify/handlers/ses/message.py +1 -1
- mojo/apps/notify/providers/aws.py +2 -2
- mojo/apps/tasks/__init__.py +34 -1
- mojo/apps/tasks/manager.py +200 -45
- mojo/apps/tasks/rest/tasks.py +24 -10
- mojo/apps/tasks/runner.py +283 -18
- mojo/apps/tasks/task.py +99 -0
- mojo/apps/tasks/tq_handlers.py +118 -0
- mojo/decorators/auth.py +6 -1
- mojo/decorators/http.py +7 -2
- mojo/helpers/aws/__init__.py +41 -0
- mojo/helpers/aws/ec2.py +804 -0
- mojo/helpers/aws/iam.py +748 -0
- mojo/helpers/aws/s3.py +451 -11
- mojo/helpers/aws/ses.py +483 -0
- mojo/helpers/aws/sns.py +461 -0
- mojo/helpers/crypto/__pycache__/hash.cpython-310.pyc +0 -0
- mojo/helpers/crypto/__pycache__/sign.cpython-310.pyc +0 -0
- mojo/helpers/crypto/__pycache__/utils.cpython-310.pyc +0 -0
- mojo/helpers/dates.py +18 -0
- mojo/helpers/response.py +6 -2
- mojo/helpers/settings/__init__.py +2 -0
- mojo/helpers/{settings.py → settings/helper.py} +1 -37
- mojo/helpers/settings/parser.py +132 -0
- mojo/middleware/logging.py +1 -1
- mojo/middleware/mojo.py +5 -0
- mojo/models/rest.py +261 -46
- mojo/models/secrets.py +13 -4
- mojo/serializers/__init__.py +100 -0
- mojo/serializers/advanced/README.md +363 -0
- mojo/serializers/advanced/__init__.py +247 -0
- mojo/serializers/advanced/formats/__init__.py +28 -0
- mojo/serializers/advanced/formats/csv.py +416 -0
- mojo/serializers/advanced/formats/excel.py +516 -0
- mojo/serializers/advanced/formats/json.py +239 -0
- mojo/serializers/advanced/formats/localizers.py +509 -0
- mojo/serializers/advanced/formats/response.py +485 -0
- mojo/serializers/advanced/serializer.py +568 -0
- mojo/serializers/manager.py +501 -0
- mojo/serializers/optimized.py +618 -0
- mojo/serializers/settings_example.py +322 -0
- mojo/serializers/{models.py → simple.py} +38 -15
- testit/helpers.py +21 -4
- django_nativemojo-0.1.10.dist-info/METADATA +0 -96
- mojo/apps/metrics/rest/db.py +0 -0
- mojo/helpers/aws/setup_email.py +0 -0
- mojo/ws4redis/README.md +0 -174
- mojo/ws4redis/__init__.py +0 -2
- mojo/ws4redis/client.py +0 -283
- mojo/ws4redis/connection.py +0 -327
- mojo/ws4redis/exceptions.py +0 -32
- mojo/ws4redis/redis.py +0 -183
- mojo/ws4redis/servers/base.py +0 -86
- mojo/ws4redis/servers/django.py +0 -171
- mojo/ws4redis/servers/uwsgi.py +0 -63
- mojo/ws4redis/settings.py +0 -45
- mojo/ws4redis/utf8validator.py +0 -128
- mojo/ws4redis/websocket.py +0 -403
- {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.15.dist-info}/LICENSE +0 -0
- {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.15.dist-info}/NOTICE +0 -0
- {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.15.dist-info}/WHEEL +0 -0
- /mojo/{ws4redis/servers → apps/aws}/__init__.py +0 -0
- /mojo/apps/{fileman/models/render.py → aws/models/__init__.py} +0 -0
- /mojo/apps/fileman/{rest/__init__ → migrations/__init__.py} +0 -0
@@ -1,5 +1,7 @@
|
|
1
|
+
import os
|
1
2
|
from django.db import models
|
2
3
|
from mojo.models import MojoModel, MojoSecrets
|
4
|
+
from urllib.parse import urlparse
|
3
5
|
|
4
6
|
|
5
7
|
class FileManager(MojoSecrets, MojoModel):
|
@@ -11,7 +13,7 @@ class FileManager(MojoSecrets, MojoModel):
|
|
11
13
|
CAN_SAVE = CAN_CREATE = True
|
12
14
|
CAN_DELETE = True
|
13
15
|
DEFAULT_SORT = "-id"
|
14
|
-
VIEW_PERMS = ["view_fileman"]
|
16
|
+
VIEW_PERMS = ["view_fileman", "manage_files"]
|
15
17
|
SEARCH_FIELDS = ["name", "backend_type", "description"]
|
16
18
|
SEARCH_TERMS = [
|
17
19
|
"name", "backend_type", "description",
|
@@ -19,12 +21,19 @@ class FileManager(MojoSecrets, MojoModel):
|
|
19
21
|
|
20
22
|
GRAPHS = {
|
21
23
|
"default": {
|
24
|
+
"fields": [
|
25
|
+
"created", "id", "name", "backend_type", "backend_url",
|
26
|
+
"settings", "is_active", "is_default"],
|
22
27
|
"graphs": {
|
28
|
+
"user": "basic",
|
23
29
|
"group": "basic"
|
24
30
|
}
|
25
31
|
},
|
26
32
|
"list": {
|
33
|
+
"fields": ["created", "id", "name", "backend_type", "backend_url",
|
34
|
+
"settings", "is_active", "is_default"],
|
27
35
|
"graphs": {
|
36
|
+
"user": "basic",
|
28
37
|
"group": "basic"
|
29
38
|
}
|
30
39
|
}
|
@@ -58,6 +67,16 @@ class FileManager(MojoSecrets, MojoModel):
|
|
58
67
|
help_text="Group that owns this file manager configuration"
|
59
68
|
)
|
60
69
|
|
70
|
+
user = models.ForeignKey(
|
71
|
+
"account.User",
|
72
|
+
related_name="file_managers",
|
73
|
+
null=True,
|
74
|
+
blank=True,
|
75
|
+
default=None,
|
76
|
+
on_delete=models.CASCADE,
|
77
|
+
help_text="User that owns this file manager configuration"
|
78
|
+
)
|
79
|
+
|
61
80
|
name = models.CharField(
|
62
81
|
max_length=255,
|
63
82
|
db_index=True,
|
@@ -88,7 +107,7 @@ class FileManager(MojoSecrets, MojoModel):
|
|
88
107
|
)
|
89
108
|
|
90
109
|
max_file_size = models.BigIntegerField(
|
91
|
-
default=
|
110
|
+
default=1000 * 1024 * 1024, # 100MB default
|
92
111
|
help_text="Maximum file size in bytes (0 for unlimited)"
|
93
112
|
)
|
94
113
|
|
@@ -114,6 +133,19 @@ class FileManager(MojoSecrets, MojoModel):
|
|
114
133
|
help_text="Whether this is the default file manager for the group or user"
|
115
134
|
)
|
116
135
|
|
136
|
+
is_public = models.BooleanField(
|
137
|
+
default=True,
|
138
|
+
help_text="Whether this allows public access to the files"
|
139
|
+
)
|
140
|
+
|
141
|
+
parent = models.ForeignKey(
|
142
|
+
'self',
|
143
|
+
on_delete=models.CASCADE,
|
144
|
+
null=True,
|
145
|
+
blank=True,
|
146
|
+
help_text="Used if this file manager is a child of another file manager, and inherits settings from its parent"
|
147
|
+
)
|
148
|
+
|
117
149
|
class Meta:
|
118
150
|
unique_together = [
|
119
151
|
['group', 'name'],
|
@@ -131,16 +163,74 @@ class FileManager(MojoSecrets, MojoModel):
|
|
131
163
|
|
132
164
|
def get_setting(self, key, default=None):
|
133
165
|
"""Get a specific setting value"""
|
134
|
-
|
166
|
+
value = self.get_secret(key, default)
|
167
|
+
if value is None:
|
168
|
+
value = self.primary_parent.get_secret(key, default)
|
169
|
+
return value
|
135
170
|
|
136
171
|
def set_setting(self, key, value):
|
137
172
|
"""Set a specific setting value"""
|
138
173
|
self.set_secret(key, value)
|
139
174
|
|
175
|
+
def set_settings(self, value):
|
176
|
+
"""Set a specific setting value"""
|
177
|
+
self.set_secrets(value)
|
178
|
+
|
179
|
+
def set_backend_url(self, url, *args):
|
180
|
+
"""Set the backend URL"""
|
181
|
+
self.backend_url = os.path.join(url, *args)
|
182
|
+
self.backend_type = self.backend_url.split(':')[0]
|
183
|
+
|
184
|
+
def _update_default(self):
|
185
|
+
if self.is_default:
|
186
|
+
if self.pk is None:
|
187
|
+
FileManager.objects.filter(
|
188
|
+
group=self.group,
|
189
|
+
user=self.user,
|
190
|
+
is_default=True
|
191
|
+
).update(is_default=False)
|
192
|
+
else:
|
193
|
+
FileManager.objects.filter(
|
194
|
+
group=self.group,
|
195
|
+
user=self.user,
|
196
|
+
is_default=True
|
197
|
+
).exclude(pk=self.pk).update(is_default=False)
|
198
|
+
|
199
|
+
_backend = None
|
200
|
+
|
201
|
+
@property
|
202
|
+
def backend(self):
|
203
|
+
"""Get the backend instance"""
|
204
|
+
from mojo.apps.fileman import backends
|
205
|
+
if not self._backend:
|
206
|
+
self._backend = backends.get_backend(self)
|
207
|
+
return self._backend
|
208
|
+
|
140
209
|
@property
|
141
210
|
def settings(self):
|
142
211
|
return self.secrets
|
143
212
|
|
213
|
+
@property
|
214
|
+
def primary_settings(self):
|
215
|
+
return self.primary_parent.secrets
|
216
|
+
|
217
|
+
@property
|
218
|
+
def primary_parent(self):
|
219
|
+
parent = self
|
220
|
+
while parent.parent:
|
221
|
+
parent = parent.parent
|
222
|
+
return parent
|
223
|
+
|
224
|
+
@property
|
225
|
+
def root_path(self):
|
226
|
+
purl = urlparse(self.backend_url)
|
227
|
+
return purl.path.lstrip('/')
|
228
|
+
|
229
|
+
@property
|
230
|
+
def root_location(self):
|
231
|
+
purl = urlparse(self.backend_url)
|
232
|
+
return purl.netloc
|
233
|
+
|
144
234
|
@property
|
145
235
|
def is_file_system(self):
|
146
236
|
return self.backend_type == self.FILE_SYSTEM
|
@@ -185,43 +275,83 @@ class FileManager(MojoSecrets, MojoModel):
|
|
185
275
|
return True
|
186
276
|
return mime_type.lower() in [mt.lower() for mt in self.allowed_mime_types]
|
187
277
|
|
188
|
-
def
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
278
|
+
def on_rest_created(self):
|
279
|
+
self._update_default()
|
280
|
+
|
281
|
+
def on_rest_pre_save(self, changed_fields, created):
|
282
|
+
self._update_default()
|
283
|
+
if not self.name:
|
284
|
+
self.name = self.generate_name()
|
285
|
+
if created or "is_default" in changed_fields:
|
286
|
+
self._update_default()
|
287
|
+
|
288
|
+
def on_rest_saved(self, changed_fields, created):
|
289
|
+
self._update_default()
|
290
|
+
if not self.name:
|
291
|
+
self.name = self.generate_name()
|
292
|
+
if "is_public" in changed_fields or created:
|
293
|
+
if self.is_public:
|
294
|
+
self.backend.make_path_public()
|
295
|
+
else:
|
296
|
+
self.backend.make_path_private()
|
297
|
+
|
298
|
+
def generate_name(self):
|
299
|
+
if self.user and self.group:
|
300
|
+
return f"{self.user.username}@{self.group.name}'s {self.backend_type} FileManager"
|
301
|
+
elif self.user:
|
302
|
+
return f"{self.user.username}'s {self.backend_type} FileManager"
|
303
|
+
elif self.group:
|
304
|
+
return f"{self.group.name}'s {self.backend_type} FileManager"
|
305
|
+
return f"{self.backend_type} FileManager"
|
198
306
|
|
199
307
|
@classmethod
|
200
308
|
def get_from_request(cls, request):
|
201
309
|
"""Get the file manager from the request"""
|
202
|
-
if request.DATA.fileman:
|
203
|
-
return cls.objects.get(pk=request.DATA.fileman)
|
204
|
-
if request.DATA.use_groups_fileman:
|
310
|
+
if request.DATA.get(["fileman", "filemanager"]):
|
311
|
+
return cls.objects.get(pk=request.DATA.get(["fileman", "filemanager"]))
|
312
|
+
if request.DATA.use_groups_fileman and request.group:
|
205
313
|
return cls.get_for_user_group(group=request.group)
|
206
314
|
return cls.get_for_user_group(user=request.user, group=request.group)
|
207
315
|
|
208
316
|
@classmethod
|
209
|
-
def
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
group=group,
|
220
|
-
|
317
|
+
def get_for_user(cls, user, group=None):
|
318
|
+
file_manager = cls.objects.filter(
|
319
|
+
user=user, group=group, is_default=True, is_active=True
|
320
|
+
).first()
|
321
|
+
if file_manager is None:
|
322
|
+
if group:
|
323
|
+
sys_manager = cls.get_for_group(group=group)
|
324
|
+
else:
|
325
|
+
sys_manager = cls.objects.filter(user=None, group=None, is_default=True, is_active=True).first()
|
326
|
+
if sys_manager is not None:
|
327
|
+
file_manager = cls(user=user, is_default=True, group=group, parent=sys_manager)
|
328
|
+
file_manager.set_backend_url(sys_manager.backend_url, user.uuid.hex)
|
329
|
+
file_manager.save()
|
330
|
+
return file_manager
|
221
331
|
|
222
|
-
|
223
|
-
|
224
|
-
|
332
|
+
@classmethod
|
333
|
+
def get_for_group(cls, group=None):
|
334
|
+
file_manager = cls.objects.filter(
|
335
|
+
user=None, group=group, is_default=True, is_active=True
|
336
|
+
).first()
|
337
|
+
if file_manager is None:
|
338
|
+
sys_manager = cls.objects.filter(
|
339
|
+
user=None, group=None, is_default=True, is_active=True
|
225
340
|
).first()
|
341
|
+
if sys_manager is not None:
|
342
|
+
file_manager = cls(group=group, is_default=True, user=None, parent=sys_manager)
|
343
|
+
file_manager.set_backend_url(sys_manager.backend_url, group.uuid.hex)
|
344
|
+
file_manager.save()
|
345
|
+
return file_manager
|
226
346
|
|
347
|
+
@classmethod
|
348
|
+
def get_for_user_group(cls, user=None, group=None):
|
349
|
+
"""Get the file manager from the user and/or group"""
|
350
|
+
file_manager = None
|
351
|
+
if user and group is None:
|
352
|
+
file_manager = cls.get_for_user(user=user)
|
353
|
+
if not file_manager and group and user is None:
|
354
|
+
file_manager = cls.get_for_user_group(group=group)
|
355
|
+
if not file_manager and group and user:
|
356
|
+
file_manager = cls.get_for_user(user=user, group=group)
|
227
357
|
return file_manager
|
@@ -0,0 +1,118 @@
|
|
1
|
+
from django.db import models
|
2
|
+
from mojo.models import MojoModel
|
3
|
+
import uuid
|
4
|
+
import hashlib
|
5
|
+
import mimetypes
|
6
|
+
from datetime import datetime
|
7
|
+
import os
|
8
|
+
from mojo.apps.fileman import utils
|
9
|
+
from mojo.apps.fileman.models import FileManager
|
10
|
+
from typing import Text
|
11
|
+
|
12
|
+
|
13
|
+
class FileRendition(models.Model, MojoModel):
|
14
|
+
"""
|
15
|
+
File model representing uploaded files with metadata and storage information
|
16
|
+
"""
|
17
|
+
|
18
|
+
class RestMeta:
|
19
|
+
CAN_SAVE = CAN_CREATE = True
|
20
|
+
CAN_DELETE = True
|
21
|
+
DEFAULT_SORT = "-created"
|
22
|
+
VIEW_PERMS = ["view_fileman", "manage_files"]
|
23
|
+
SEARCH_FIELDS = ["filename", "content_type"]
|
24
|
+
SEARCH_TERMS = [
|
25
|
+
"filename", "content_type",
|
26
|
+
("group", "group__name"),
|
27
|
+
("file_manager", "file_manager__name")]
|
28
|
+
|
29
|
+
GRAPHS = {
|
30
|
+
"upload": {
|
31
|
+
"fields": ["id", "filename", "content_type", "file_size"],
|
32
|
+
},
|
33
|
+
"default": {
|
34
|
+
"extra": ["url"],
|
35
|
+
},
|
36
|
+
"list": {
|
37
|
+
"extra": ["url"],
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
# Upload status choices
|
42
|
+
PENDING = 'pending'
|
43
|
+
RENDERING = 'rendering'
|
44
|
+
COMPLETED = 'completed'
|
45
|
+
FAILED = 'failed'
|
46
|
+
EXPIRED = 'expired'
|
47
|
+
|
48
|
+
created = models.DateTimeField(auto_now_add=True, editable=False, db_index=True)
|
49
|
+
modified = models.DateTimeField(auto_now=True)
|
50
|
+
|
51
|
+
original_file = models.ForeignKey(
|
52
|
+
"fileman.File",
|
53
|
+
related_name="file_renditions",
|
54
|
+
on_delete=models.CASCADE,
|
55
|
+
help_text="The parent file"
|
56
|
+
)
|
57
|
+
|
58
|
+
filename = models.CharField(
|
59
|
+
max_length=255,
|
60
|
+
db_index=True,
|
61
|
+
help_text="rendition filename"
|
62
|
+
)
|
63
|
+
|
64
|
+
storage_path = models.TextField(
|
65
|
+
help_text="Storage path and filename",
|
66
|
+
)
|
67
|
+
|
68
|
+
download_url = models.TextField(
|
69
|
+
blank=True,
|
70
|
+
null=True,
|
71
|
+
default=None,
|
72
|
+
help_text="Persistent URL for downloading the file, (if allowed)"
|
73
|
+
)
|
74
|
+
|
75
|
+
file_size = models.BigIntegerField(
|
76
|
+
null=True,
|
77
|
+
blank=True,
|
78
|
+
help_text="File size in bytes"
|
79
|
+
)
|
80
|
+
|
81
|
+
content_type = models.CharField(
|
82
|
+
max_length=255,
|
83
|
+
help_text="MIME type of the file"
|
84
|
+
)
|
85
|
+
|
86
|
+
category = models.CharField(
|
87
|
+
max_length=255,
|
88
|
+
help_text="A category for the file, like 'image', 'document', 'video', etc."
|
89
|
+
)
|
90
|
+
|
91
|
+
role = models.CharField(
|
92
|
+
max_length=255,
|
93
|
+
db_index=True,
|
94
|
+
help_text="The role of the file, like 'thumbnail', 'preview', 'full', etc."
|
95
|
+
)
|
96
|
+
|
97
|
+
upload_status = models.CharField(
|
98
|
+
max_length=32,
|
99
|
+
default=PENDING,
|
100
|
+
db_index=True,
|
101
|
+
help_text="Current status of rendering"
|
102
|
+
)
|
103
|
+
|
104
|
+
@property
|
105
|
+
def file_manager(self):
|
106
|
+
return self.original_file.file_manager
|
107
|
+
|
108
|
+
@property
|
109
|
+
def url(self):
|
110
|
+
return self.generate_download_url()
|
111
|
+
|
112
|
+
def generate_download_url(self):
|
113
|
+
if self.download_url:
|
114
|
+
return self.download_url
|
115
|
+
if self.file_manager.is_public:
|
116
|
+
self.download_url = self.file_manager.backend.get_url(self.storage_path)
|
117
|
+
return self.download_url
|
118
|
+
return self.file_manager.backend.get_url(self.storage_path, self.get_setting("urls_expire_in", 3600))
|
@@ -0,0 +1,111 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import Dict, List, Optional, Tuple, Any, Union
|
3
|
+
|
4
|
+
# Import renderer classes
|
5
|
+
from mojo.apps.fileman.models import File, FileRendition
|
6
|
+
from mojo.apps.fileman.renderer.base import BaseRenderer, RenditionRole
|
7
|
+
from mojo.apps.fileman.renderer.image import ImageRenderer
|
8
|
+
from mojo.apps.fileman.renderer.video import VideoRenderer
|
9
|
+
from mojo.apps.fileman.renderer.document import DocumentRenderer
|
10
|
+
from mojo.apps.fileman.renderer.audio import AudioRenderer
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
# Register renderers in order of preference
|
15
|
+
RENDERERS = [
|
16
|
+
ImageRenderer,
|
17
|
+
VideoRenderer,
|
18
|
+
DocumentRenderer,
|
19
|
+
AudioRenderer,
|
20
|
+
]
|
21
|
+
|
22
|
+
__all__ = [
|
23
|
+
'BaseRenderer', 'RenditionRole', 'ImageRenderer', 'VideoRenderer', 'DocumentRenderer', 'AudioRenderer',
|
24
|
+
'get_renderer_for_file', 'create_rendition', 'create_all_renditions',
|
25
|
+
'get_rendition', 'get_or_create_rendition'
|
26
|
+
]
|
27
|
+
|
28
|
+
def get_renderer_for_file(file: File) -> Optional[BaseRenderer]:
|
29
|
+
"""
|
30
|
+
Get the appropriate renderer for a file based on its category
|
31
|
+
|
32
|
+
Args:
|
33
|
+
file: The file to get a renderer for
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
BaseRenderer: The renderer instance, or None if no renderer supports the file
|
37
|
+
"""
|
38
|
+
for renderer_class in RENDERERS:
|
39
|
+
if renderer_class.supports_file(file):
|
40
|
+
return renderer_class(file)
|
41
|
+
return None
|
42
|
+
|
43
|
+
def create_rendition(file: File, role: str, options: Dict = None) -> Optional[FileRendition]:
|
44
|
+
"""
|
45
|
+
Create a rendition for a file
|
46
|
+
|
47
|
+
Args:
|
48
|
+
file: The file to create a rendition for
|
49
|
+
role: The role of the rendition (e.g., 'thumbnail', 'preview')
|
50
|
+
options: Additional options for creating the rendition
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
FileRendition: The created rendition, or None if creation failed
|
54
|
+
"""
|
55
|
+
renderer = get_renderer_for_file(file)
|
56
|
+
if not renderer:
|
57
|
+
logger.warning(f"No renderer available for file {file.id} ({file.filename}, {file.category})")
|
58
|
+
return None
|
59
|
+
|
60
|
+
return renderer.create_rendition(role, options)
|
61
|
+
|
62
|
+
def create_all_renditions(file: File) -> List[FileRendition]:
|
63
|
+
"""
|
64
|
+
Create all default renditions for a file
|
65
|
+
|
66
|
+
Args:
|
67
|
+
file: The file to create renditions for
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
List[FileRendition]: List of created renditions
|
71
|
+
"""
|
72
|
+
renderer = get_renderer_for_file(file)
|
73
|
+
if not renderer:
|
74
|
+
logger.warning(f"No renderer available for file {file.id} ({file.filename}, {file.category})")
|
75
|
+
return []
|
76
|
+
|
77
|
+
return renderer.create_all_renditions()
|
78
|
+
|
79
|
+
def get_rendition(file: File, role: str) -> Optional[FileRendition]:
|
80
|
+
"""
|
81
|
+
Get an existing rendition for a file
|
82
|
+
|
83
|
+
Args:
|
84
|
+
file: The file to get a rendition for
|
85
|
+
role: The role of the rendition
|
86
|
+
|
87
|
+
Returns:
|
88
|
+
FileRendition: The rendition, or None if not found
|
89
|
+
"""
|
90
|
+
try:
|
91
|
+
return FileRendition.objects.get(original_file=file, role=role)
|
92
|
+
except FileRendition.DoesNotExist:
|
93
|
+
return None
|
94
|
+
|
95
|
+
def get_or_create_rendition(file: File, role: str, options: Dict = None) -> Optional[FileRendition]:
|
96
|
+
"""
|
97
|
+
Get an existing rendition or create a new one if it doesn't exist
|
98
|
+
|
99
|
+
Args:
|
100
|
+
file: The file to get or create a rendition for
|
101
|
+
role: The role of the rendition
|
102
|
+
options: Additional options for creating the rendition
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
FileRendition: The rendition, or None if not found and creation failed
|
106
|
+
"""
|
107
|
+
rendition = get_rendition(file, role)
|
108
|
+
if rendition:
|
109
|
+
return rendition
|
110
|
+
|
111
|
+
return create_rendition(file, role, options)
|