photo-objects 0.9.3__py3-none-any.whl → 0.9.5__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.
@@ -1,3 +1,5 @@
1
+ from threading import Thread
2
+
1
3
  from django.contrib.auth import get_user_model
2
4
  from django.contrib.auth.models import Group, Permission
3
5
  from django.contrib.sites.models import Site
@@ -56,18 +58,11 @@ def _group_dict(group: Group):
56
58
  }
57
59
 
58
60
 
59
- def create_backup(backup: Backup):
60
- # Check if backup already exists
61
- if get_backup_object(backup.id):
62
- backup.status = "ready"
63
- backup.save()
64
- return
61
+ def _create_backup(backup_id: int):
62
+ backup = Backup.objects.get(id=backup_id)
65
63
 
66
64
  user_model = get_user_model()
67
65
 
68
- backup.status = "pending"
69
- backup.save()
70
-
71
66
  try:
72
67
  albums = Album.objects.all()
73
68
  for album in albums:
@@ -140,6 +135,21 @@ def create_backup(backup: Backup):
140
135
  backup.save()
141
136
 
142
137
 
138
+ def create_backup(backup: Backup):
139
+ # Check if backup already exists
140
+ if get_backup_object(backup.id):
141
+ backup.status = "ready"
142
+ backup.save()
143
+ return
144
+
145
+ backup.status = "pending"
146
+ backup.save()
147
+
148
+ # Start creating backup in a separate thread
149
+ thread = Thread(target=_create_backup, args=(backup.id,))
150
+ thread.start()
151
+
152
+
143
153
  def delete_backup(backup: Backup):
144
154
  return delete_backup_objects(backup.id)
145
155
 
@@ -50,7 +50,7 @@ class JsonProblem(PhotoObjectsError):
50
50
  return render(request, "photo_objects/problem.html", {
51
51
  "title": "Error",
52
52
  "back": BackLink(
53
- 'Back to albums',
53
+ 'Albums',
54
54
  reverse_lazy('photo_objects:list_albums')),
55
55
  "problem_title": self.title,
56
56
  "status": self.status
@@ -42,9 +42,9 @@ def _objsto_access() -> tuple[dict, Minio]:
42
42
  )
43
43
 
44
44
  return (conf, Minio(
45
- conf.get('URL'),
46
- conf.get('ACCESS_KEY'),
47
- conf.get('SECRET_KEY'),
45
+ endpoint=conf.get('URL'),
46
+ access_key=conf.get('ACCESS_KEY'),
47
+ secret_key=conf.get('SECRET_KEY'),
48
48
  http_client=http,
49
49
  secure=conf.get('SECURE', True),
50
50
  ))
@@ -55,8 +55,8 @@ def _backup_access() -> tuple[Minio, str]:
55
55
  bucket = conf.get('BACKUP_BUCKET', 'backups')
56
56
 
57
57
  # TODO: move this to management command
58
- if not client.bucket_exists(bucket):
59
- client.make_bucket(bucket)
58
+ if not client.bucket_exists(bucket_name=bucket):
59
+ client.make_bucket(bucket_name=bucket)
60
60
 
61
61
  return client, bucket
62
62
 
@@ -66,9 +66,12 @@ def _photos_access() -> tuple[Minio, str]:
66
66
  bucket = conf.get('BUCKET', 'photos')
67
67
 
68
68
  # TODO: move this to management command
69
- if not client.bucket_exists(bucket):
70
- client.make_bucket(bucket)
71
- client.set_bucket_policy(bucket, _anonymous_readonly_policy(bucket))
69
+ if not client.bucket_exists(bucket_name=bucket):
70
+ client.make_bucket(bucket_name=bucket)
71
+ client.set_bucket_policy(
72
+ bucket_name=bucket,
73
+ policy=_anonymous_readonly_policy(bucket),
74
+ )
72
75
 
73
76
  return client, bucket
74
77
 
@@ -79,9 +82,9 @@ def _put_json(key, data, access_fn):
79
82
 
80
83
  client, bucket = access_fn()
81
84
  client.put_object(
82
- bucket,
83
- key,
84
- stream,
85
+ bucket_name=bucket,
86
+ object_name=key,
87
+ data=stream,
85
88
  length=-1,
86
89
  part_size=10 * MEGABYTE,
87
90
  content_type="application/json",
@@ -92,7 +95,7 @@ def _list_all(client: Minio, bucket: str, prefix: str):
92
95
  start_after = None
93
96
  while True:
94
97
  objects = client.list_objects(
95
- bucket,
98
+ bucket_name=bucket,
96
99
  prefix=prefix,
97
100
  recursive=True,
98
101
  start_after=start_after)
@@ -112,12 +115,18 @@ def _list_all(client: Minio, bucket: str, prefix: str):
112
115
 
113
116
  def _get_all(client: Minio, bucket: str, prefix: str):
114
117
  for i in _list_all(client, bucket, prefix):
115
- yield client.get_object(bucket, i.object_name)
118
+ yield client.get_object(
119
+ bucket_name=bucket,
120
+ object_name=i.object_name,
121
+ )
116
122
 
117
123
 
118
124
  def _delete_all(client: Minio, bucket: str, prefix: str):
119
125
  for i in _list_all(client, bucket, prefix):
120
- client.remove_object(bucket, i.object_name)
126
+ client.remove_object(
127
+ bucket_name=bucket,
128
+ object_name=i.object_name,
129
+ )
121
130
  yield i.object_name
122
131
 
123
132
 
@@ -141,7 +150,10 @@ def get_backup_object(backup_id: int):
141
150
  client, bucket = _backup_access()
142
151
 
143
152
  try:
144
- data = client.get_object(bucket, backup_info_key(backup_id))
153
+ data = client.get_object(
154
+ bucket_name=bucket,
155
+ object_name=backup_info_key(backup_id),
156
+ )
145
157
  return json.loads(data.read())
146
158
  except S3Error as e:
147
159
  if e.code == "NoSuchKey":
@@ -162,7 +174,10 @@ def get_backup_data(id_: int, type_=None):
162
174
 
163
175
  def delete_backup_objects(id_: int):
164
176
  client, bucket = _backup_access()
165
- client.remove_object(bucket, backup_info_key(id_))
177
+ client.remove_object(
178
+ bucket_name=bucket,
179
+ object_name=backup_info_key(id_),
180
+ )
166
181
  for _ in _delete_all(client, bucket, backup_data_prefix(id_)):
167
182
  continue
168
183
 
@@ -198,9 +213,9 @@ def put_photo(album_key, photo_key, size_key, photo_file, image_format=None):
198
213
 
199
214
  client, bucket = _photos_access()
200
215
  return client.put_object(
201
- bucket,
202
- photo_path(album_key, photo_key, size_key),
203
- photo_file,
216
+ bucket_name=bucket,
217
+ object_name=photo_path(album_key, photo_key, size_key),
218
+ data=photo_file,
204
219
  length=-1,
205
220
  part_size=10 * MEGABYTE,
206
221
  content_type=content_type,
@@ -211,8 +226,8 @@ def put_photo(album_key, photo_key, size_key, photo_file, image_format=None):
211
226
  def get_photo(album_key, photo_key, size_key):
212
227
  client, bucket = _photos_access()
213
228
  return client.get_object(
214
- bucket,
215
- photo_path(album_key, photo_key, size_key)
229
+ bucket_name=bucket,
230
+ object_name=photo_path(album_key, photo_key, size_key)
216
231
  )
217
232
 
218
233
 
@@ -220,7 +235,10 @@ def delete_photo(album_key, photo_key):
220
235
  client, bucket = _photos_access()
221
236
 
222
237
  for i in PhotoSize:
223
- client.remove_object(bucket, photo_path(album_key, photo_key, i.value))
238
+ client.remove_object(
239
+ bucket_name=bucket,
240
+ object_name=photo_path(album_key, photo_key, i.value),
241
+ )
224
242
 
225
243
 
226
244
  def delete_scaled_photos(sizes):
@@ -251,7 +269,10 @@ def put_photo_sizes(sizes: PhotoSizes):
251
269
  def get_photo_sizes() -> PhotoSizes:
252
270
  client, bucket = _photos_access()
253
271
  try:
254
- data = client.get_object(bucket, "photo_sizes.json")
272
+ data = client.get_object(
273
+ bucket_name=bucket,
274
+ object_name="photo_sizes.json",
275
+ )
255
276
  return parse_photo_sizes(json.loads(data.read()))
256
277
  except S3Error as e:
257
278
  if e.code == "NoSuchKey":
@@ -40,7 +40,7 @@ def new_album(request: HttpRequest):
40
40
  else:
41
41
  form = CreateAlbumForm(initial={"key": "_new"}, user=request.user)
42
42
 
43
- back = BackLink("Back to albums", reverse('photo_objects:list_albums'))
43
+ back = BackLink("Albums", reverse('photo_objects:list_albums'))
44
44
 
45
45
  return render(request, 'photo_objects/form.html', {
46
46
  "form": form,
@@ -59,7 +59,7 @@ def show_album(request: HttpRequest, album_key: str):
59
59
  album = api.check_album_access(request, album_key)
60
60
  photos = album.photo_set.all()
61
61
 
62
- back = BackLink("Back to albums", reverse('photo_objects:list_albums'))
62
+ back = BackLink("Albums", reverse('photo_objects:list_albums'))
63
63
  details = {
64
64
  "Description": render_markdown(album.description),
65
65
  "Visibility": Album.Visibility(album.visibility).label,
@@ -102,7 +102,7 @@ def edit_album(request: HttpRequest, album_key: str):
102
102
 
103
103
  target = album.title or album.key
104
104
  back = BackLink(
105
- f'Back to {target}',
105
+ target,
106
106
  reverse(
107
107
  'photo_objects:show_album',
108
108
  kwargs={"album_key": album_key}))
@@ -125,7 +125,7 @@ def delete_album(request: HttpRequest, album_key: str):
125
125
  album = api.check_album_access(request, album_key)
126
126
  target = album.title or album.key
127
127
  back = BackLink(
128
- f'Back to {target}',
128
+ target,
129
129
  reverse(
130
130
  'photo_objects:show_album',
131
131
  kwargs={
@@ -167,7 +167,7 @@ def configuration(request: HttpRequest):
167
167
  site_description_configured(request),
168
168
  ]
169
169
 
170
- back = BackLink("Back to albums", reverse('photo_objects:list_albums'))
170
+ back = BackLink("Albums", reverse('photo_objects:list_albums'))
171
171
 
172
172
  return render(request, "photo_objects/configuration.html", {
173
173
  "title": "Configuration",
@@ -43,7 +43,7 @@ def upload_photos(request: HttpRequest, album_key: str):
43
43
  album = api.check_album_access(request, album_key)
44
44
  target = album.title or album.key
45
45
  back = BackLink(
46
- f"Back to {target}", reverse(
46
+ target, reverse(
47
47
  'photo_objects:show_album', kwargs={
48
48
  "album_key": album_key}))
49
49
 
@@ -109,7 +109,7 @@ def show_photo(request: HttpRequest, album_key: str, photo_key: str):
109
109
 
110
110
  previous_filename = photo.key.split("/")[-1]
111
111
  next_filename = previous_filename
112
- back = BackLink("Back to albums", reverse('photo_objects:list_albums'))
112
+ back = BackLink("Albums", reverse('photo_objects:list_albums'))
113
113
 
114
114
  try:
115
115
  api.check_album_access(request, photo.album.key)
@@ -125,7 +125,7 @@ def show_photo(request: HttpRequest, album_key: str, photo_key: str):
125
125
 
126
126
  target = photo.album.title or photo.album.key
127
127
  back = BackLink(
128
- f"Back to {target}", reverse(
128
+ target, reverse(
129
129
  'photo_objects:show_album', kwargs={
130
130
  "album_key": album_key}))
131
131
  except AlbumNotFound:
@@ -170,7 +170,7 @@ def edit_photo(request: HttpRequest, album_key: str, photo_key: str):
170
170
 
171
171
  target = photo.title or photo.filename
172
172
  back = BackLink(
173
- f'Back to {target}',
173
+ target,
174
174
  reverse(
175
175
  'photo_objects:show_photo',
176
176
  kwargs={
@@ -197,7 +197,7 @@ def delete_photo(request: HttpRequest, album_key: str, photo_key: str):
197
197
  photo = api.check_photo_access(request, album_key, photo_key, "xs")
198
198
  target = photo.title or photo.filename
199
199
  back = BackLink(
200
- f'Back to {target}',
200
+ target,
201
201
  reverse(
202
202
  'photo_objects:show_photo',
203
203
  kwargs={
@@ -47,7 +47,7 @@ def review_photo_change_request(request: HttpRequest, cr_id: str):
47
47
  else:
48
48
  info = f"There are {count} change requests in the review queue."
49
49
 
50
- back = BackLink("Back to albums", reverse('photo_objects:list_albums'))
50
+ back = BackLink("Albums", reverse('photo_objects:list_albums'))
51
51
 
52
52
  return render(request, 'photo_objects/form.html', {
53
53
  "form": form,
@@ -16,7 +16,7 @@ def login(request: HttpRequest):
16
16
  "photo": settings.preview_image,
17
17
  "action": "Login",
18
18
  "back": BackLink(
19
- 'Back to albums',
19
+ 'Albums',
20
20
  reverse_lazy('photo_objects:list_albums')),
21
21
  "class": "login"
22
22
  },
@@ -6,7 +6,7 @@ from photo_objects.utils import first_paragraph_textcontent
6
6
 
7
7
 
8
8
  class BackLink:
9
- def __init__(self, text, url):
9
+ def __init__(self, text: str, url: str):
10
10
  self.text = text
11
11
  self.url = url
12
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: photo-objects
3
- Version: 0.9.3
3
+ Version: 0.9.5
4
4
  Summary: Application for storing photos in S3 compatible object-storage.
5
5
  Author: Toni Kangas
6
6
  License: MIT License
@@ -42,7 +42,7 @@ Requires-Python: >=3.10
42
42
  Description-Content-Type: text/markdown
43
43
  License-File: LICENSE
44
44
  Requires-Dist: markdown~=3.7
45
- Requires-Dist: minio~=7.2
45
+ Requires-Dist: minio<7.2.19,>=7.2.0
46
46
  Requires-Dist: pillow~=10.4
47
47
  Dynamic: license-file
48
48
 
@@ -10,16 +10,16 @@ photo_objects/django/conf.py,sha256=ZpeIulEc1tpr8AO52meNKOF30Xf5osbDtDyHvQRtkx4,
10
10
  photo_objects/django/context_processors.py,sha256=XacUmcYV-4NMMMNXPWrHKdvNd6lfyamisngaVerREiU,306
11
11
  photo_objects/django/forms.py,sha256=zSSmdIYn4PEx1Nnw-tuo4h4otKj3C099lB4-vp2BRUc,6861
12
12
  photo_objects/django/models.py,sha256=OxkkczIBg7TaFWWm4VdtbRMJOK_OQKrV29g-X2cm5BQ,7247
13
- photo_objects/django/objsto.py,sha256=jDk5o1b0TZdRA5ie8rbEB4B7y9yG2FzYJGqUPER5JNk,6340
13
+ photo_objects/django/objsto.py,sha256=kf-Tv-kDt47Mnx0xMolAct_lP4M0H9xg03M3BnfAnJM,6909
14
14
  photo_objects/django/signals.py,sha256=_gb4vlZkeFNYWXxwhNreaUJoOsbIWvP8OovVLtzepaE,2161
15
15
  photo_objects/django/urls.py,sha256=XnOSEB8YtAJamlEsjKYz6U1DfDu7HHZpAinpqdulR8k,2501
16
16
  photo_objects/django/api/__init__.py,sha256=51CRTiE975ufVhvI5x-M_2D28JP8FZWyLFiuV5EaQSg,120
17
17
  photo_objects/django/api/album.py,sha256=CJDeGLCuYoxGUDcjssZRpFnToxG_KVE9Ii7NduFW2ks,2003
18
18
  photo_objects/django/api/auth.py,sha256=lS0S1tMVH2uN30g4jlixklv3eMnQ2FbQVQvuRXeMGYo,1420
19
- photo_objects/django/api/backup.py,sha256=_D1KMkIp7A8iPs2LxQ-MsaFcAHwCwhE7un5NRHHIvOc,6099
19
+ photo_objects/django/api/backup.py,sha256=lu-lDSGpEV9ASCIA5o0kNOZcg6_cmkVPCy1TFRvYyyY,6344
20
20
  photo_objects/django/api/photo.py,sha256=-lo1g6jfBr884wy-WV2DAEPxzH9V-tFUTRtitmA6i28,4471
21
21
  photo_objects/django/api/photo_change_request.py,sha256=v94RA7SUM60tC9mIZdz8HppbNKfHWeTFNPr_BPw3pys,3075
22
- photo_objects/django/api/utils.py,sha256=8r51YgFgKPD05Zjzhstl4jlQ4JM8DtsxUyAzhjXi5Pk,5567
22
+ photo_objects/django/api/utils.py,sha256=M7GdTZAkeAWSRjZeI51dlv8qaw2CjX3ruKsa1amc1Es,5559
23
23
  photo_objects/django/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  photo_objects/django/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  photo_objects/django/management/commands/clean-scaled-photos.py,sha256=KJY6phgTCxcmbMUsUfCRQjatvCmKyFninM8zT-tB3Kc,2008
@@ -46,21 +46,21 @@ photo_objects/django/tests/test_photo_change_requests.py,sha256=Ld5ytqxxZiEWrqfX
46
46
  photo_objects/django/tests/test_utils.py,sha256=0Xl0ReS2X9GsEH-jmLg8xC79hJ4nhbfULo0BOVjiykQ,2089
47
47
  photo_objects/django/tests/utils.py,sha256=LiObyRARkmO4arnY2gXNi_T8XxT9eSKKszENMo2UIh8,4639
48
48
  photo_objects/django/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
- photo_objects/django/views/utils.py,sha256=AjJK5r5HmTF63E9Q4W3pKggDESuhNXUvbROpS8m70KM,1319
49
+ photo_objects/django/views/utils.py,sha256=TSs3RTtSi_4GcgeM8I3i36-sga95JV0VD0nYK4EZkWk,1329
50
50
  photo_objects/django/views/api/__init__.py,sha256=GgywMJMSFmP5aoMEaYut5V666zachd5YFQIDBMr5znU,188
51
51
  photo_objects/django/views/api/album.py,sha256=EZMOkxYzLSWr9wwXnd4yAO64JtXZq2k3FYohiNMFbGQ,1602
52
52
  photo_objects/django/views/api/auth.py,sha256=EN_ExegzmLN-bhSzu3L9-6UE9qodPd7_ZRLilzrvc8Y,819
53
53
  photo_objects/django/views/api/photo.py,sha256=WHSayWTi_wG6otq6Rz1IKqJ5ik5riclR3AWB15ge5RU,4613
54
54
  photo_objects/django/views/api/utils.py,sha256=uQzKdSKHRAux5OZzqgWQr0gsK_FeweQP0cg_67OWA_Y,264
55
55
  photo_objects/django/views/ui/__init__.py,sha256=Y3XrckZExbHpWVNwDUGLfb99_midb8-5j6Ouf_Yu_G4,128
56
- photo_objects/django/views/ui/album.py,sha256=WmWrbY3nPCK7NIZYE9rJPzC39HjwN-TW8fN4gxIC3Yk,4765
57
- photo_objects/django/views/ui/configuration.py,sha256=jyZT3ZAzKa7RnzhED5anDShRCBiCHxOU81Cddt10-4Q,5096
58
- photo_objects/django/views/ui/photo.py,sha256=flwcET5nSChzfyAEWRTUlklTW2o64przNXSdWn-jxLw,6676
59
- photo_objects/django/views/ui/photo_change_request.py,sha256=eaYGXFqtHj8qonDAmPyn4nrEHkL13EBD-1s8Phs0XP4,2098
60
- photo_objects/django/views/ui/users.py,sha256=nb73cnzuV98QkJb0j8F2hqPgOGFIWpUFTFu6dXMeVwM,722
56
+ photo_objects/django/views/ui/album.py,sha256=b07jboWsiRJUHC1BPLC8vmNbpePjtFzKzbc9QbBoPYM,4723
57
+ photo_objects/django/views/ui/configuration.py,sha256=PIKqjTJ8szVlLodo9rU6XA2hh9FXrSSpnKWgUFY8E8g,5088
58
+ photo_objects/django/views/ui/photo.py,sha256=erk1-b6A3pr1XMMcEmB1PcjFysvbfiLhfXx1yrKTCdI,6616
59
+ photo_objects/django/views/ui/photo_change_request.py,sha256=CFBV-O9k7WzdeYXYB7N7_g5xHw_seh9-Dzis8M6zbOo,2090
60
+ photo_objects/django/views/ui/users.py,sha256=XOVqOgLwxD2QKua_bPl7wZUNrBd_bABbCaHYLSnhMI0,714
61
61
  photo_objects/django/views/ui/utils.py,sha256=YV_YcUbX-zUkdFnBlezPChR6aPDhZJ9loSOHBSzF6Cc,273
62
- photo_objects-0.9.3.dist-info/licenses/LICENSE,sha256=V3w6hTjXfP65F4r_mejveHcV5igHrblxao3-2RlfVlA,1068
63
- photo_objects-0.9.3.dist-info/METADATA,sha256=gWQW91vUQX-zsuk3XK16hBx2q1h2llDSizZtJnUnXN4,3605
64
- photo_objects-0.9.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
65
- photo_objects-0.9.3.dist-info/top_level.txt,sha256=SZeL8mhf-WMGdhRtTGFvZc3aIRBboQls9O0cFDMGdQ0,14
66
- photo_objects-0.9.3.dist-info/RECORD,,
62
+ photo_objects-0.9.5.dist-info/licenses/LICENSE,sha256=V3w6hTjXfP65F4r_mejveHcV5igHrblxao3-2RlfVlA,1068
63
+ photo_objects-0.9.5.dist-info/METADATA,sha256=8gbXtVaHTTijCVmKHr-9ru8AdYYdGeyOe2Ol_Pdo5xc,3615
64
+ photo_objects-0.9.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
65
+ photo_objects-0.9.5.dist-info/top_level.txt,sha256=SZeL8mhf-WMGdhRtTGFvZc3aIRBboQls9O0cFDMGdQ0,14
66
+ photo_objects-0.9.5.dist-info/RECORD,,