Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/data redirects #483

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
17 changes: 14 additions & 3 deletions api/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections import Sequence

from django.shortcuts import get_object_or_404
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect
from rest_framework import viewsets
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
Expand All @@ -9,7 +10,7 @@
from api.versioning import check_api_version_redirect
from core.filters import parse_querystring
from core.forms import get_table_dynamic_form
from core.models import Dataset, Table
from core.models import Dataset, DataUrlRedirect, Table
from core.templatetags.utils import obfuscate

from . import paginators
Expand All @@ -21,7 +22,14 @@ class DatasetViewSet(viewsets.ModelViewSet):

@check_api_version_redirect
def retrieve(self, request, slug):
obj = get_object_or_404(self.get_queryset(), slug=slug)
try:
obj = self.get_queryset().get(slug=slug)
except Dataset.DoesNotExist:
redirect_url = DataUrlRedirect.redirect_from(request)
if redirect_url:
return redirect(redirect_url)
raise Http404

serializer = DatasetDetailSerializer(obj, context=self.get_serializer_context(),)
return Response(serializer.data)

Expand Down Expand Up @@ -96,6 +104,9 @@ def handle_exception(self, exc):

@check_api_version_redirect
def get(self, *args, **kwargs):
redirect_url = DataUrlRedirect.redirect_from(self.request)
if redirect_url:
return redirect(redirect_url)
return super().get(*args, **kwargs)


Expand Down
13 changes: 13 additions & 0 deletions core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,17 @@ def has_delete_permission(self, request, *args, **kwargs):
return request.user.is_superuser


class DataUrlRedirectAdmin(admin.ModelAdmin):
list_display = [
"id",
"dataset_prev",
"dataset_dest",
"tablename_prev",
"tablename_dest",
"field_prev",
"field_dest",
]


admin.site.register(models.TableFile, TableFileAdmin)
admin.site.register(models.DataUrlRedirect, DataUrlRedirectAdmin)
25 changes: 25 additions & 0 deletions core/migrations/0028_auto_20201016_1519.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 3.1.1 on 2020-10-16 18:19

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("core", "0027_auto_20201007_1707"),
]

operations = [
migrations.CreateModel(
name="DataUrlRedirect",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("dataset_prev", models.SlugField(default="")),
("dataset_dest", models.SlugField(default="")),
("tablename_prev", models.SlugField(default="")),
("tablename_dest", models.SlugField(default="")),
("field_prev", models.SlugField(default="")),
("field_dest", models.SlugField(default="")),
],
),
]
89 changes: 88 additions & 1 deletion core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import random
import string
from collections import OrderedDict, namedtuple
from copy import deepcopy
from textwrap import dedent
from urllib.parse import urlparse
from urllib.parse import urlencode, urlparse

import django.contrib.postgres.indexes as pg_indexes
import django.db.models.indexes as django_indexes
Expand Down Expand Up @@ -675,3 +676,89 @@ def readable_size(self):
@property
def admin_url(self):
return reverse("admin:core_tablefile_change", args=[self.id])


class DataUrlRedirect(models.Model):
dataset_prev = models.SlugField(default="", blank=True)
dataset_dest = models.SlugField(default="", blank=True)
tablename_prev = models.SlugField(default="", blank=True)
tablename_dest = models.SlugField(default="", blank=True)
field_prev = models.SlugField(default="", blank=True)
field_dest = models.SlugField(default="", blank=True)

@property
def redirect_map(self):
map = {}
dataset_url_names = [
"core:dataset-detail",
"core:dataset-files-detail",
"api-v1:dataset-detail",
]

if self.dataset_prev != self.dataset_dest:
map.update(
**{
reverse(n, args=[self.dataset_prev]): reverse(n, args=[self.dataset_dest])
for n in dataset_url_names
}
)

if self.tablename_dest != self.tablename_prev:
table_url_names = [
"core:dataset-table-detail",
"api-v1:dataset-table-data",
]
map.update(
**{
reverse(n, args=[self.dataset_prev, self.tablename_prev]): reverse(
n, args=[self.dataset_dest, self.tablename_dest]
)
for n in table_url_names
}
)

return map

@classmethod
def redirect_from(cls, request):
path = request.path
redirects = {}
fields_map = {}

for r in cls.objects.all().iterator():
redirects.update(**r.redirect_map)

if r.field_prev != r.field_dest:
key = (r.dataset_dest, r.tablename_dest, r.field_prev)
fields_map[key] = r.field_dest

# Order prefixes begining by the most complex ones
redirect_url = ""
for url_prefix in sorted(redirects, reverse=True):
if path.startswith(url_prefix):
redirect_url_prefix = redirects[url_prefix]
redirect_url = path.replace(url_prefix, redirect_url_prefix)
break

qs = ""
query_params = getattr(request, "GET", {})
redirect_qs = deepcopy(query_params)
if query_params:
url = redirect_url or path
for key in fields_map:
dataset, tablename, field_prev = key
has_field_update = all([f"/{dataset}/" in url, f"/{tablename}/" in url, field_prev in redirect_qs])
if has_field_update:
value = redirect_qs[field_prev]
redirect_qs.pop(field_prev)
redirect_qs[fields_map[key]] = value

qs = urlencode(redirect_qs)

if not redirect_url and qs and set(query_params.keys()) != set(redirect_qs.keys()):
redirect_url = path

if not redirect_url:
return ""

return redirect_url + (f"?{qs}" if qs else "")
Loading