django-async-upload-kg 4.0.1__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 (24) hide show
  1. django-async-upload-kg-4.0.1/LICENSE +20 -0
  2. django-async-upload-kg-4.0.1/MANIFEST.in +3 -0
  3. django-async-upload-kg-4.0.1/PKG-INFO +82 -0
  4. django-async-upload-kg-4.0.1/README.rst +62 -0
  5. django-async-upload-kg-4.0.1/admin_async_upload/__init__.py +0 -0
  6. django-async-upload-kg-4.0.1/admin_async_upload/fields.py +14 -0
  7. django-async-upload-kg-4.0.1/admin_async_upload/files.py +150 -0
  8. django-async-upload-kg-4.0.1/admin_async_upload/models.py +15 -0
  9. django-async-upload-kg-4.0.1/admin_async_upload/static/admin_resumable/js/resumable.js +1071 -0
  10. django-async-upload-kg-4.0.1/admin_async_upload/storage.py +49 -0
  11. django-async-upload-kg-4.0.1/admin_async_upload/templates/admin_resumable/admin_file_input.html +142 -0
  12. django-async-upload-kg-4.0.1/admin_async_upload/templates/admin_resumable/user_file_input.html +61 -0
  13. django-async-upload-kg-4.0.1/admin_async_upload/urls.py +7 -0
  14. django-async-upload-kg-4.0.1/admin_async_upload/validators.py +68 -0
  15. django-async-upload-kg-4.0.1/admin_async_upload/views.py +39 -0
  16. django-async-upload-kg-4.0.1/admin_async_upload/widgets.py +87 -0
  17. django-async-upload-kg-4.0.1/django_async_upload_kg.egg-info/PKG-INFO +82 -0
  18. django-async-upload-kg-4.0.1/django_async_upload_kg.egg-info/SOURCES.txt +23 -0
  19. django-async-upload-kg-4.0.1/django_async_upload_kg.egg-info/dependency_links.txt +1 -0
  20. django-async-upload-kg-4.0.1/django_async_upload_kg.egg-info/requires.txt +1 -0
  21. django-async-upload-kg-4.0.1/django_async_upload_kg.egg-info/top_level.txt +1 -0
  22. django-async-upload-kg-4.0.1/setup.cfg +7 -0
  23. django-async-upload-kg-4.0.1/setup.py +44 -0
  24. django-async-upload-kg-4.0.1/tests/test_uploads.py +214 -0
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 jonatron
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include admin_resumable/templates/admin_resumable/file_input.html
3
+ include admin_resumable/static/admin_resumable/js/resumable.js
@@ -0,0 +1,82 @@
1
+ Metadata-Version: 2.1
2
+ Name: django-async-upload-kg
3
+ Version: 4.0.1
4
+ Summary: A Django app for the uploading of large files from the django admin site.
5
+ Home-page: https://github.com/bit/django-async-upload
6
+ Author: Kajala Group Ltd.
7
+ Author-email: support@kajala.com
8
+ License: MIT License
9
+ Classifier: Environment :: Web Environment
10
+ Classifier: Framework :: Django
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Topic :: Internet :: WWW/HTTP
17
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
18
+ License-File: LICENSE
19
+ Requires-Dist: Django>=3.0.14
20
+
21
+ django-async-upload
22
+ ===============================
23
+
24
+ Based on fork from https://github.com/bit/django-async-upload. Allows install via pip:
25
+
26
+ pip install django-async-upload-kg
27
+
28
+ django-async-upload is a django app to allow you to upload large files from within the django admin site asynchrously, that means that you can add any number of files on the admin page (e.g. through inline models) and continue editing other fields while files are uploading.
29
+
30
+ django-async-upload is compatible with django-storages (tested with S3Storage)
31
+
32
+
33
+ Installation
34
+ ------------
35
+
36
+ * pip install django-async-upload
37
+ * Add ``admin_async_upload`` to your ``INSTALLED_APPS``
38
+ * Add ``url(r'^admin_async_upload/', include('admin_async_upload.urls')),`` to your urls.py
39
+ * Add a model field eg: ``from admin_resumable.models import ResumableFileField``
40
+
41
+ ::
42
+
43
+ class Foo(models.Model):
44
+ bar = models.CharField(max_length=200)
45
+ foo = AsyncFileField()
46
+
47
+
48
+
49
+ Optionally:
50
+
51
+ * Set ``ADMIN_RESUMABLE_CHUNKSIZE``, default is ``"1*1024*1024"``
52
+ * Set ``ADMIN_RESUMABLE_STORAGE``, default is setting of DEFAULT_FILE_STORAGE and ultimately ``'django.core.files.storage.FileSystemStorage'``. If you don't want the default FileSystemStorage behaviour of creating new files on the server with filenames appended with _1, _2, etc for consecutive uploads of the same file, then you could use this to set your storage class to something like https://djangosnippets.org/snippets/976/
53
+ * Set ``ADMIN_RESUMABLE_CHUNK_STORAGE``, default is ``'django.core.files.storage.FileSystemStorage'`` . If you don't want the default FileSystemStorage behaviour of creating new files on the server with filenames appended with _1, _2, etc for consecutive uploads of the same file, then you could use this to set your storage class to something like https://djangosnippets.org/snippets/976/
54
+ * Set ``ADMIN_RESUMABLE_SHOW_THUMB``, default is False. Shows a thumbnail next to the "Currently:" link.
55
+ * Set ``ADMIN_SIMULTANEOUS_UPLOADS`` to limit number of simulteneous uploads, dedaults to `3`. If you have broken pipe issues in local development environment, set this value to `1`.
56
+
57
+
58
+ Versions
59
+ --------
60
+
61
+ 4.0.0 - inital fork of django-admin-async-upload 3.0.4 with support for Django 4 and later
62
+
63
+
64
+ Compatibility
65
+ -------------
66
+
67
+ Tested on Django 4.2 running on python 3.11
68
+
69
+ Thanks to
70
+ ---------
71
+
72
+ original django-admin-resumable-js by jonatron https://github.com/jonatron/django-admin-resumable-js
73
+
74
+ django-admin-resumable-js fork by roxel https://github.com/roxel/django-admin-resumable-js
75
+
76
+ django-admin-async-upload fork by DataGreed https://github.com/DataGreed/django-admin-async-upload
77
+
78
+ Resumable.js https://github.com/23/resumable.js
79
+
80
+ django-resumable https://github.com/jeanphix/django-resumable
81
+
82
+
@@ -0,0 +1,62 @@
1
+ django-async-upload
2
+ ===============================
3
+
4
+ Based on fork from https://github.com/bit/django-async-upload. Allows install via pip:
5
+
6
+ pip install django-async-upload-kg
7
+
8
+ django-async-upload is a django app to allow you to upload large files from within the django admin site asynchrously, that means that you can add any number of files on the admin page (e.g. through inline models) and continue editing other fields while files are uploading.
9
+
10
+ django-async-upload is compatible with django-storages (tested with S3Storage)
11
+
12
+
13
+ Installation
14
+ ------------
15
+
16
+ * pip install django-async-upload
17
+ * Add ``admin_async_upload`` to your ``INSTALLED_APPS``
18
+ * Add ``url(r'^admin_async_upload/', include('admin_async_upload.urls')),`` to your urls.py
19
+ * Add a model field eg: ``from admin_resumable.models import ResumableFileField``
20
+
21
+ ::
22
+
23
+ class Foo(models.Model):
24
+ bar = models.CharField(max_length=200)
25
+ foo = AsyncFileField()
26
+
27
+
28
+
29
+ Optionally:
30
+
31
+ * Set ``ADMIN_RESUMABLE_CHUNKSIZE``, default is ``"1*1024*1024"``
32
+ * Set ``ADMIN_RESUMABLE_STORAGE``, default is setting of DEFAULT_FILE_STORAGE and ultimately ``'django.core.files.storage.FileSystemStorage'``. If you don't want the default FileSystemStorage behaviour of creating new files on the server with filenames appended with _1, _2, etc for consecutive uploads of the same file, then you could use this to set your storage class to something like https://djangosnippets.org/snippets/976/
33
+ * Set ``ADMIN_RESUMABLE_CHUNK_STORAGE``, default is ``'django.core.files.storage.FileSystemStorage'`` . If you don't want the default FileSystemStorage behaviour of creating new files on the server with filenames appended with _1, _2, etc for consecutive uploads of the same file, then you could use this to set your storage class to something like https://djangosnippets.org/snippets/976/
34
+ * Set ``ADMIN_RESUMABLE_SHOW_THUMB``, default is False. Shows a thumbnail next to the "Currently:" link.
35
+ * Set ``ADMIN_SIMULTANEOUS_UPLOADS`` to limit number of simulteneous uploads, dedaults to `3`. If you have broken pipe issues in local development environment, set this value to `1`.
36
+
37
+
38
+ Versions
39
+ --------
40
+
41
+ 4.0.0 - inital fork of django-admin-async-upload 3.0.4 with support for Django 4 and later
42
+
43
+
44
+ Compatibility
45
+ -------------
46
+
47
+ Tested on Django 4.2 running on python 3.11
48
+
49
+ Thanks to
50
+ ---------
51
+
52
+ original django-admin-resumable-js by jonatron https://github.com/jonatron/django-admin-resumable-js
53
+
54
+ django-admin-resumable-js fork by roxel https://github.com/roxel/django-admin-resumable-js
55
+
56
+ django-admin-async-upload fork by DataGreed https://github.com/DataGreed/django-admin-async-upload
57
+
58
+ Resumable.js https://github.com/23/resumable.js
59
+
60
+ django-resumable https://github.com/jeanphix/django-resumable
61
+
62
+
@@ -0,0 +1,14 @@
1
+ from django.core.exceptions import ValidationError
2
+ from django.forms import fields
3
+
4
+ from admin_async_upload.widgets import ResumableAdminWidget
5
+
6
+
7
+ class FormResumableFileField(fields.FileField):
8
+ widget = ResumableAdminWidget
9
+
10
+ def to_python(self, data):
11
+ if self.required:
12
+ if not data or data == "None":
13
+ raise ValidationError(self.error_messages['empty'])
14
+ return data
@@ -0,0 +1,150 @@
1
+ # -*- coding: utf-8 -*-
2
+ import fnmatch
3
+ import tempfile
4
+
5
+ from django.contrib.contenttypes.models import ContentType
6
+ from django.core.files import File
7
+ from django.utils.functional import cached_property
8
+
9
+ from admin_async_upload.storage import ResumableStorage
10
+
11
+
12
+ class ResumableFile(object):
13
+ """
14
+ Handles file saving and processing.
15
+ It must only have access to chunk storage where it saves file chunks.
16
+ When all chunks are uploaded it collects and merges them returning temporary file pointer
17
+ that can be used to save the complete file to persistent storage.
18
+
19
+ Chunk storage should preferably be some local storage to avoid traffic
20
+ as files usually must be downloaded to server as chunks and re-uploaded as complete files.
21
+ """
22
+
23
+ def __init__(self, field, user, params):
24
+ self.field = field
25
+ self.user = user
26
+ self.params = params
27
+ self.chunk_suffix = "_part_"
28
+
29
+ @cached_property
30
+ def resumable_storage(self):
31
+ return ResumableStorage()
32
+
33
+ @cached_property
34
+ def persistent_storage(self):
35
+ return self.resumable_storage.get_persistent_storage()
36
+
37
+ @cached_property
38
+ def chunk_storage(self):
39
+ return ResumableStorage().get_chunk_storage()
40
+
41
+ @property
42
+ def storage_filename(self):
43
+ instance_id = self.params.get("instance_id")
44
+ if instance_id:
45
+ instance = self.field.model.objects.filter(pk=instance_id).first()
46
+ else:
47
+ instance = None
48
+ return self.resumable_storage.full_filename(self.filename, self.upload_to, instance=instance)
49
+
50
+ @property
51
+ def upload_to(self):
52
+ return self.field.upload_to
53
+
54
+ @property
55
+ def chunk_exists(self):
56
+ """
57
+ Checks if the requested chunk exists.
58
+ """
59
+ return self.chunk_storage.exists(self.current_chunk_name) and \
60
+ self.chunk_storage.size(self.current_chunk_name) == int(self.params.get('resumableCurrentChunkSize'))
61
+
62
+ @property
63
+ def chunk_names(self):
64
+ """
65
+ Iterates over all stored chunks.
66
+ """
67
+ chunks = []
68
+ files = sorted(self.chunk_storage.listdir('')[1])
69
+ for file in files:
70
+ if fnmatch.fnmatch(file, '%s%s*' % (self.filename,
71
+ self.chunk_suffix)):
72
+ chunks.append(file)
73
+ return chunks
74
+
75
+ @property
76
+ def current_chunk_name(self):
77
+ # TODO: add user identifier to chunk name
78
+ return "%s%s%s" % (
79
+ self.filename,
80
+ self.chunk_suffix,
81
+ self.params.get('resumableChunkNumber').zfill(4)
82
+ )
83
+
84
+ def chunks(self):
85
+ """
86
+ Iterates over all stored chunks.
87
+ """
88
+ # TODO: add user identifier to chunk name
89
+ files = sorted(self.chunk_storage.listdir('')[1])
90
+ for file in files:
91
+ if fnmatch.fnmatch(file, '%s%s*' % (self.filename,
92
+ self.chunk_suffix)):
93
+ yield self.chunk_storage.open(file, 'rb').read()
94
+
95
+ def delete_chunks(self):
96
+ [self.chunk_storage.delete(chunk) for chunk in self.chunk_names]
97
+
98
+ @property
99
+ def file(self):
100
+ """
101
+ Merges file and returns its file pointer.
102
+ """
103
+ if not self.is_complete:
104
+ raise Exception('Chunk(s) still missing')
105
+ outfile = tempfile.NamedTemporaryFile("w+b")
106
+ for chunk in self.chunk_names:
107
+ outfile.write(self.chunk_storage.open(chunk).read())
108
+ return outfile
109
+
110
+ @property
111
+ def filename(self):
112
+ """
113
+ Gets the filename.
114
+ """
115
+ # TODO: add user identifier to chunk name
116
+ filename = self.params.get('resumableFilename')
117
+ if '/' in filename:
118
+ raise Exception('Invalid filename')
119
+ value = "%s_%s" % (self.params.get('resumableTotalSize'), filename)
120
+ return value
121
+
122
+ @property
123
+ def is_complete(self):
124
+ """
125
+ Checks if all chunks are already stored.
126
+ """
127
+ return int(self.params.get('resumableTotalSize')) == self.size
128
+
129
+ def process_chunk(self, file):
130
+ """
131
+ Saves chunk to chunk storage.
132
+ """
133
+ if self.chunk_storage.exists(self.current_chunk_name):
134
+ self.chunk_storage.delete(self.current_chunk_name)
135
+ self.chunk_storage.save(self.current_chunk_name, file)
136
+
137
+ @property
138
+ def size(self):
139
+ """
140
+ Gets size of all chunks combined.
141
+ """
142
+ size = 0
143
+ for chunk in self.chunk_names:
144
+ size += self.chunk_storage.size(chunk)
145
+ return size
146
+
147
+ def collect(self):
148
+ actual_filename = self.persistent_storage.save(self.storage_filename, File(self.file))
149
+ self.delete_chunks()
150
+ return actual_filename
@@ -0,0 +1,15 @@
1
+ from django.db import models
2
+ from admin_async_upload.widgets import ResumableAdminWidget
3
+ from admin_async_upload.fields import FormResumableFileField
4
+
5
+
6
+ class AsyncFileField(models.FileField):
7
+
8
+ def formfield(self, **kwargs):
9
+ defaults = {'form_class': FormResumableFileField}
10
+ if self.model and self.name:
11
+ defaults['widget'] = ResumableAdminWidget(attrs={
12
+ 'model': self.model,
13
+ 'field_name': self.name})
14
+ kwargs.update(defaults)
15
+ return super(AsyncFileField, self).formfield(**kwargs)