Skip to content

Commit

Permalink
feat: resolve upstream redirects
Browse files Browse the repository at this point in the history
  • Loading branch information
japsu committed Aug 12, 2024
1 parent aa751ec commit d3d3d80
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 31 deletions.
42 changes: 42 additions & 0 deletions backend/edegal/models/album.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,48 @@ def get_album_by_path(cls, path, or_404=False, **extra_criteria):
else:
return queryset.get()

@classmethod
def resolve_upstream_redirects(cls, path, **extra_criteria):
"""
Given a path, if there is a redirect higher up in the hierarchy, return a fake album dict
that redirects to the correct path. Otherwise return None.
"""
# TODO cache?
parts = path.strip("/").split("/")
rewritten_parts = []
rewrites_done = False

for part in parts:
original_path = "/" + "/".join(rewritten_parts + [part])

try:
album = cls.get_album_by_path(original_path, **extra_criteria)
except cls.DoesNotExist:
return None

if redirect_url := album.redirect_url:
if redirect_url.startswith("/"):
# Local album redirect – resolve further
rewritten_parts = album.redirect_url.strip("/").split("/")
rewrites_done = True
else:
# External redirect – can't resolve further
return cls.fake_album_as_dict(path=path, redirect_url=redirect_url)
elif album.path == original_path:
# path refers to an album
rewritten_parts.append(album.slug)
else:
# path refers to a picture or a technical view
rewritten_parts.append(part)

if rewrites_done:
return cls.fake_album_as_dict(
path=path,
redirect_url="/" + "/".join(rewritten_parts),
)

return None

def get_download_file_path(self, prefix=settings.MEDIA_ROOT + "/"):
return f"{prefix}downloads{self.path}.zip"

Expand Down
2 changes: 2 additions & 0 deletions backend/edegal/models/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class Series(AlbumMixin, models.Model):
settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL
)

redirect_url = ""

def save(self, *args, **kwargs):
if self.title and not self.slug:
self.slug = slugify(self.title)
Expand Down
97 changes: 69 additions & 28 deletions backend/edegal/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,60 @@

from django.test import TestCase

from .models import Album, Picture, MediaSpec, Media
from .models import Album, Media, MediaSpec, Picture


class AlbumTestCase(TestCase):
def setUp(self):
root, unused = Album.objects.get_or_create(
path='',
path="",
defaults=dict(
title='My Swell Picture Gallery',
title="My Swell Picture Gallery",
),
)

album1, unused = Album.objects.get_or_create(
path='/album-1',
path="/album-1",
defaults=dict(
title='Album, the First of his Name',
slug='album-1',
title="Album, the First of his Name",
slug="album-1",
parent=root,
)
),
)

album2, unused = Album.objects.get_or_create(
path='/album-2',
path="/album-2",
defaults=dict(
title='Album 2',
title="Album 2",
parent=root,
)
),
)

picture1, unused = Picture.objects.get_or_create(
path='/album-1/picture-1',
path="/album-1/picture-1",
defaults=dict(
title='Picture 1',
title="Picture 1",
album=album2,
)
),
)

picture2, unused = Picture.objects.get_or_create(
path='/album-1/picture-2',
path="/album-1/picture-2",
defaults=dict(
title='Picture 2',
title="Picture 2",
album=album2,
)
),
)

original_media, unused = Media.objects.get_or_create(
picture=picture1,
spec=None,
defaults=dict(
src=str(uuid4()),
role='original',
role="original",
width=800,
height=600,
)
),
)

spec, unused = MediaSpec.objects.get_or_create(
Expand All @@ -69,28 +69,69 @@ def setUp(self):
spec=spec,
defaults=dict(
src=str(uuid4()),
role='thumbnail',
role="thumbnail",
width=spec.max_width,
height=spec.max_height,
)
),
)

def test_get_album_by_path(self):
album = Album.get_album_by_path('/album-1')
self.assertEqual(album.path, '/album-1')
album = Album.get_album_by_path("/album-1")
self.assertEqual(album.path, "/album-1")

album = Album.get_album_by_path('/album-2/picture-2')
self.assertEqual(album.path, '/album-2')
album = Album.get_album_by_path("/album-2/picture-2")
self.assertEqual(album.path, "/album-2")

def test_as_dict(self):
album = Album.get_album_by_path('/album-2')
album = Album.get_album_by_path("/album-2")
print(album.as_dict())

def test_canonical_path(self):
picture1 = Picture.objects.get(path='/album-2/picture-1')
picture1 = Picture.objects.get(path="/album-2/picture-1")

original = picture1.original
self.assertEqual(original.get_canonical_path(prefix=''), 'pictures/album-2/picture-1.jpeg')
self.assertEqual(
original.get_canonical_path(prefix=""), "pictures/album-2/picture-1.jpeg"
)

derived = picture1.media.get(spec__max_width=640, spec__max_height=480)
self.assertEqual(derived.get_canonical_path(prefix=''), 'previews/album-2/picture-1.thumbnail.jpeg')
self.assertEqual(
derived.get_canonical_path(prefix=""),
"previews/album-2/picture-1.thumbnail.jpeg",
)

def test_resolve_redirects(self):
"""
If an upstream album has a redirect URL to another album in this gallery,
handle its descendants as if they were in the target album.
"""
root_album = Album.objects.get(path="/")

Album.objects.create(
title="Album 3",
redirect_url="/album-4",
parent=root_album,
)

album4 = Album.objects.create(
title="Album 4",
parent=root_album,
)

child_album = Album.objects.create(
title="Child Album",
parent=album4,
)

Picture.objects.create(
album=child_album,
title="Picture",
)

album_dict = Album.resolve_upstream_redirects("/album-3/child-album")
assert album_dict is not None
self.assertEqual(album_dict["path"], "/album-4/child-album")

album_dict = Album.resolve_upstream_redirects("/album-3/child-album/picture")
assert album_dict is not None
self.assertEqual(album_dict["path"], "/album-4/child-album/picture")
19 changes: 16 additions & 3 deletions backend/edegal/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from .models import Album, Photographer, Picture
from .models.media_spec import FORMAT_CHOICES


SUPPORTED_FORMATS = {format for (format, disp) in FORMAT_CHOICES}


Expand All @@ -31,7 +30,19 @@ def get(self, request, path):
if not request.user.is_staff:
extra_criteria.update(is_public=True)

album = Album.get_album_by_path(path, or_404=True, **extra_criteria)
try:
album = Album.get_album_by_path(path, **extra_criteria)
except Album.DoesNotExist:
if redirect_dict := Album.resolve_upstream_redirects(
path, **extra_criteria
):
return JsonResponse(redirect_dict)
else:
return JsonResponse(
{"status": 404, "message": "album not found"},
status=404,
)

response = JsonResponse(
album.as_dict(
include_hidden=request.user.is_staff,
Expand Down Expand Up @@ -59,7 +70,9 @@ def get(self, request):
body=pseudoalbum.body if pseudoalbum else "",
subalbums=[
photog.make_subalbum()
for photog in Photographer.objects.filter(cover_picture__media__isnull=False).distinct()
for photog in Photographer.objects.filter(
cover_picture__media__isnull=False
).distinct()
],
pictures=[],
breadcrumb=[
Expand Down

0 comments on commit d3d3d80

Please sign in to comment.