From 0d14ffa4b8a05cb74fe27fadf210388805502d54 Mon Sep 17 00:00:00 2001 From: ata Date: Mon, 17 Feb 2020 18:34:25 +0300 Subject: [PATCH 01/14] Initial history api --- project/api/admin.py | 25 ++++++------ project/api/urls.py | 2 + project/api/views/endpoints/__init__.py | 1 + project/api/views/endpoints/history.py | 54 +++++++++++++++++++++++++ project/chron/settings.py | 3 ++ 5 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 project/api/views/endpoints/history.py diff --git a/project/api/admin.py b/project/api/admin.py index f82575c6..6369ddf2 100644 --- a/project/api/admin.py +++ b/project/api/admin.py @@ -18,6 +18,7 @@ """ from django.contrib import admin +from simple_history.admin import SimpleHistoryAdmin from .models import ( TerritorialEntity, @@ -35,15 +36,15 @@ ) # Register your models here. -admin.site.register(TerritorialEntity) -admin.site.register(PoliticalRelation) -admin.site.register(SpacetimeVolume) -admin.site.register(CachedData) -admin.site.register(Narration) -admin.site.register(Narrative) -admin.site.register(MapSettings) -admin.site.register(City) -admin.site.register(Profile) -admin.site.register(NarrativeVote) -admin.site.register(Symbol) -admin.site.register(SymbolFeature) +admin.site.register(TerritorialEntity, SimpleHistoryAdmin) +admin.site.register(PoliticalRelation, SimpleHistoryAdmin) +admin.site.register(SpacetimeVolume, SimpleHistoryAdmin) +admin.site.register(CachedData, SimpleHistoryAdmin) +admin.site.register(Narration, SimpleHistoryAdmin) +admin.site.register(Narrative, SimpleHistoryAdmin) +admin.site.register(MapSettings, SimpleHistoryAdmin) +admin.site.register(City, SimpleHistoryAdmin) +admin.site.register(Profile, SimpleHistoryAdmin) +admin.site.register(NarrativeVote, SimpleHistoryAdmin) +admin.site.register(Symbol, SimpleHistoryAdmin) +admin.site.register(SymbolFeature, SimpleHistoryAdmin) diff --git a/project/api/urls.py b/project/api/urls.py index aebaa8d5..4163334d 100644 --- a/project/api/urls.py +++ b/project/api/urls.py @@ -54,5 +54,7 @@ ), path("mvt/stv///", views.mvt_stv, name="mvt-stv"), path("spacetime-volumes//download", views.stv_downloader), + path("history/", views.te_history), + path("history", views.all_history), path("", include(ROUTER.urls)), ] diff --git a/project/api/views/endpoints/__init__.py b/project/api/views/endpoints/__init__.py index 80ad121b..52adc3bd 100644 --- a/project/api/views/endpoints/__init__.py +++ b/project/api/views/endpoints/__init__.py @@ -5,3 +5,4 @@ from .mvt_narratives import * from .mvt_stv import * from .stv_downloader import * +from .history import * \ No newline at end of file diff --git a/project/api/views/endpoints/history.py b/project/api/views/endpoints/history.py new file mode 100644 index 00000000..aa016a2f --- /dev/null +++ b/project/api/views/endpoints/history.py @@ -0,0 +1,54 @@ +""" +Chron. +Copyright (C) 2020 Alisa Belyaeva, Ata Ali Kilicli, Amaury Martiny, +Daniil Mordasov, Liam O’Flynn, Mikhail Orlov. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from django.core.paginator import Paginator +from django.http import HttpResponse +from django.core.serializers import serialize +from api.models import SpacetimeVolume, TerritorialEntity + + +def all_history(request): + return HttpResponse("OK") + +def te_history(request, TE): + + page_number = request.GET.get('page') + limit = request.GET.get('limit') + + history = SpacetimeVolume.history.filter(entity=TE) + + + for x in history: + print(x) + + + if limit is not None: + paginator = Paginator(history, limit) + else: + paginator = Paginator(history, 25) + + if page_number is not None: + page = paginator.get_page(page_number) + else: + page = paginator.get_page("1") + + json = serialize('json', page) + + + return HttpResponse(json, content_type='application/json') \ No newline at end of file diff --git a/project/chron/settings.py b/project/chron/settings.py index 3705dd82..59bc4239 100644 --- a/project/chron/settings.py +++ b/project/chron/settings.py @@ -60,6 +60,7 @@ "drf_firebase_auth", "ordered_model", "silk", + "simple_history" ] if DEBUG: @@ -77,6 +78,8 @@ # "django.contrib.auth.middleware.RemoteUserMiddleware", # "django.middleware.clickjacking.XFrameOptionsMiddleware", "chron.middleware.ErrorMessageFormatter", + "simple_history.middleware.HistoryRequestMiddleware", + ] if DEBUG: From 204b52bb755b4d45c948f436ef551411ec81cb9c Mon Sep 17 00:00:00 2001 From: ata Date: Mon, 17 Feb 2020 19:27:55 +0300 Subject: [PATCH 02/14] New parameters --- project/api/urls.py | 4 +-- project/api/views/endpoints/history.py | 49 ++++++++++++++++++++------ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/project/api/urls.py b/project/api/urls.py index 4163334d..2ee66e3d 100644 --- a/project/api/urls.py +++ b/project/api/urls.py @@ -54,7 +54,7 @@ ), path("mvt/stv///", views.mvt_stv, name="mvt-stv"), path("spacetime-volumes//download", views.stv_downloader), - path("history/", views.te_history), - path("history", views.all_history), + path("spacetime-volumes/history/", views.stv_history), + path("spacetime-volumes/history/", views.all_stv_history), path("", include(ROUTER.urls)), ] diff --git a/project/api/views/endpoints/history.py b/project/api/views/endpoints/history.py index aa016a2f..ed899a2e 100644 --- a/project/api/views/endpoints/history.py +++ b/project/api/views/endpoints/history.py @@ -23,20 +23,50 @@ from api.models import SpacetimeVolume, TerritorialEntity -def all_history(request): - return HttpResponse("OK") - -def te_history(request, TE): - +def all_stv_history(request): + """ + Return revision history for all STVs. + Parameters + 1) limit: Limit the amount of results per page + 2) page: Page number + 3) entity: Filter by TerritorialEntity + """ page_number = request.GET.get('page') limit = request.GET.get('limit') + entity = request.GET.get('entity') - history = SpacetimeVolume.history.filter(entity=TE) + if entity is not None: + history = SpacetimeVolume.history.filter(entity=entity) + else: + history = SpacetimeVolume.history.all() + if limit is not None: + paginator = Paginator(history, limit) + else: + paginator = Paginator(history, 25) + + if page_number is not None: + page = paginator.get_page(page_number) + else: + page = paginator.get_page("1") - for x in history: - print(x) + json = serialize('json', page, fields=["id", "start_date", "end_date", "references", "history_date", "history_change_reason", "history_type", "history_user"]) + + + return HttpResponse(json, content_type='application/json') +def stv_history(request, STV): + """ + Return revision history for specific STV. + Parameters + 1) limit: Limit the amount of results per page + 2) page: Page number + """ + + page_number = request.GET.get('page') + limit = request.GET.get('limit') + + history = SpacetimeVolume.history.filter(id=STV) if limit is not None: paginator = Paginator(history, limit) @@ -48,7 +78,6 @@ def te_history(request, TE): else: page = paginator.get_page("1") - json = serialize('json', page) - + json = serialize('json', page, fields=["id", "start_date", "end_date", "references", "history_date", "history_change_reason", "history_type", "history_user"]) return HttpResponse(json, content_type='application/json') \ No newline at end of file From 8ba68892fd67512774da9e873ad4dd50d6edd199 Mon Sep 17 00:00:00 2001 From: ata Date: Tue, 18 Feb 2020 11:57:02 +0300 Subject: [PATCH 03/14] Move stv history api to viewset --- project/api/serializers.py | 20 +++++++ project/api/urls.py | 3 +- project/api/views/endpoints/history.py | 83 -------------------------- project/api/views/views.py | 40 +++++++++++++ 4 files changed, 61 insertions(+), 85 deletions(-) delete mode 100644 project/api/views/endpoints/history.py diff --git a/project/api/serializers.py b/project/api/serializers.py index 4f81a674..a8823745 100644 --- a/project/api/serializers.py +++ b/project/api/serializers.py @@ -40,6 +40,7 @@ Narration, NarrativeVote, Profile, + HistoricalSpacetimeVolume, ) @@ -252,3 +253,22 @@ def get_narration_count(self, obj): # pylint: disable=R0201 """ return obj.narration_set.count() + + +class StvHistoryListSerializer(ModelSerializer): + """ + Serializes the HistoricalSpacetimeVolume model + """ + + class Meta: + model = HistoricalSpacetimeVolume + exclude = ["territory"] + +class StvHistoryRetrieveSerializer(ModelSerializer): + """ + Serializes the HistoricalSpacetimeVolume model + """ + + class Meta: + model = HistoricalSpacetimeVolume + fields = "__all__" diff --git a/project/api/urls.py b/project/api/urls.py index 2ee66e3d..0a490c29 100644 --- a/project/api/urls.py +++ b/project/api/urls.py @@ -35,6 +35,7 @@ ROUTER.register(r"profiles", views.ProfileViewSet) ROUTER.register(r"symbols", views.SymbolViewSet) ROUTER.register(r"symbol-features", views.SymbolFeatureViewSet) +ROUTER.register(r"stv-history", views.StvHistoryViewSet) urlpatterns = [ path( @@ -54,7 +55,5 @@ ), path("mvt/stv///", views.mvt_stv, name="mvt-stv"), path("spacetime-volumes//download", views.stv_downloader), - path("spacetime-volumes/history/", views.stv_history), - path("spacetime-volumes/history/", views.all_stv_history), path("", include(ROUTER.urls)), ] diff --git a/project/api/views/endpoints/history.py b/project/api/views/endpoints/history.py deleted file mode 100644 index ed899a2e..00000000 --- a/project/api/views/endpoints/history.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -Chron. -Copyright (C) 2020 Alisa Belyaeva, Ata Ali Kilicli, Amaury Martiny, -Daniil Mordasov, Liam O’Flynn, Mikhail Orlov. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -from django.core.paginator import Paginator -from django.http import HttpResponse -from django.core.serializers import serialize -from api.models import SpacetimeVolume, TerritorialEntity - - -def all_stv_history(request): - """ - Return revision history for all STVs. - Parameters - 1) limit: Limit the amount of results per page - 2) page: Page number - 3) entity: Filter by TerritorialEntity - """ - page_number = request.GET.get('page') - limit = request.GET.get('limit') - entity = request.GET.get('entity') - - if entity is not None: - history = SpacetimeVolume.history.filter(entity=entity) - else: - history = SpacetimeVolume.history.all() - - if limit is not None: - paginator = Paginator(history, limit) - else: - paginator = Paginator(history, 25) - - if page_number is not None: - page = paginator.get_page(page_number) - else: - page = paginator.get_page("1") - - json = serialize('json', page, fields=["id", "start_date", "end_date", "references", "history_date", "history_change_reason", "history_type", "history_user"]) - - - return HttpResponse(json, content_type='application/json') - -def stv_history(request, STV): - """ - Return revision history for specific STV. - Parameters - 1) limit: Limit the amount of results per page - 2) page: Page number - """ - - page_number = request.GET.get('page') - limit = request.GET.get('limit') - - history = SpacetimeVolume.history.filter(id=STV) - - if limit is not None: - paginator = Paginator(history, limit) - else: - paginator = Paginator(history, 25) - - if page_number is not None: - page = paginator.get_page(page_number) - else: - page = paginator.get_page("1") - - json = serialize('json', page, fields=["id", "start_date", "end_date", "references", "history_date", "history_change_reason", "history_type", "history_user"]) - - return HttpResponse(json, content_type='application/json') \ No newline at end of file diff --git a/project/api/views/views.py b/project/api/views/views.py index 709caed2..c9da6bd1 100644 --- a/project/api/views/views.py +++ b/project/api/views/views.py @@ -21,6 +21,7 @@ from rest_framework import viewsets, status from rest_framework.response import Response from rest_framework.permissions import IsAuthenticatedOrReadOnly +from rest_framework.pagination import LimitOffsetPagination from api.models import ( TerritorialEntity, @@ -34,6 +35,7 @@ Profile, Symbol, SymbolFeature, + HistoricalSpacetimeVolume, ) from api.serializers import ( TerritorialEntitySerializer, @@ -47,6 +49,8 @@ ProfileSerializer, SymbolSerializer, SymbolFeatureSerializer, + StvHistoryListSerializer, + StvHistoryRetrieveSerializer, ) from api.permissions import IsUserOrReadOnly @@ -203,3 +207,39 @@ class ProfileViewSet(viewsets.ModelViewSet): queryset = Profile.objects.all() serializer_class = ProfileSerializer permission_classes = (IsAuthenticatedOrReadOnly, IsUserOrReadOnly) + + +class HistoryPagination(LimitOffsetPagination): + page_size = 100 + page_size_query_param = 'limit' + max_page_size = 1000 +class StvHistoryViewSet(viewsets.ReadOnlyModelViewSet): + """ + ViewSet for SpacetimeVolume History + """ + queryset = HistoricalSpacetimeVolume.objects.all() + pagination_class = HistoryPagination + + + def get_queryset(self): + + queryset = self.queryset + + if self.action == 'list': + stv = self.request.query_params.get('stv', None) + + if stv is not None: + queryset = queryset.filter(id=stv) + + te = self.request.query_params.get('te', None) + + if te is not None: + queryset = queryset.filter(entity=te) + + return queryset + + def get_serializer_class(self): + if self.action == 'list': + return StvHistoryListSerializer + if self.action == 'retrieve': + return StvHistoryRetrieveSerializer From 9db300463f060c17d0db5f84e23ab67dbffee1f9 Mon Sep 17 00:00:00 2001 From: ata Date: Tue, 18 Feb 2020 12:02:08 +0300 Subject: [PATCH 04/14] Removed history import --- project/api/views/endpoints/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/project/api/views/endpoints/__init__.py b/project/api/views/endpoints/__init__.py index 52adc3bd..fe81ce3d 100644 --- a/project/api/views/endpoints/__init__.py +++ b/project/api/views/endpoints/__init__.py @@ -4,5 +4,4 @@ from .mvt_cities import * from .mvt_narratives import * from .mvt_stv import * -from .stv_downloader import * -from .history import * \ No newline at end of file +from .stv_downloader import * \ No newline at end of file From 84e646abcf1c8ebbd6807c15148f60e07bd80475 Mon Sep 17 00:00:00 2001 From: ata Date: Wed, 19 Feb 2020 18:36:29 +0300 Subject: [PATCH 05/14] Added TE history api and usernames. --- project/api/serializers.py | 63 ++++++++++++++++++++++ project/api/urls.py | 2 + project/api/views/endpoints/__init__.py | 2 +- project/api/views/views.py | 71 +++++++++++++++++++------ project/chron/settings.py | 3 +- 5 files changed, 123 insertions(+), 18 deletions(-) diff --git a/project/api/serializers.py b/project/api/serializers.py index a8823745..f7bb98bf 100644 --- a/project/api/serializers.py +++ b/project/api/serializers.py @@ -41,6 +41,7 @@ NarrativeVote, Profile, HistoricalSpacetimeVolume, + HistoricalTerritorialEntity, ) @@ -260,15 +261,77 @@ class StvHistoryListSerializer(ModelSerializer): Serializes the HistoricalSpacetimeVolume model """ + history_user = SerializerMethodField() + history_user_id = SerializerMethodField() + + def get_history_user(self, obj): # pylint: disable=R0201 + """ + Returns the username of the user who made the change. + """ + + return obj.history_user.username + + def get_history_user_id(self, obj): # pylint: disable=R0201 + """ + Returns the id of the user who made the change. + """ + + return obj.history_user.id + class Meta: model = HistoricalSpacetimeVolume exclude = ["territory"] + class StvHistoryRetrieveSerializer(ModelSerializer): """ Serializes the HistoricalSpacetimeVolume model """ + history_user = SerializerMethodField() + history_user_id = SerializerMethodField() + + def get_history_user(self, obj): # pylint: disable=R0201 + """ + Returns the username of the user who made the change. + """ + + return obj.history_user.username + + def get_history_user_id(self, obj): # pylint: disable=R0201 + """ + Returns the id of the user who made the change. + """ + + return obj.history_user.id + class Meta: model = HistoricalSpacetimeVolume fields = "__all__" + + +class TeHistorySerializer(ModelSerializer): + """ + Serializes the HistoricalTerritorialEntity model + """ + + history_user = SerializerMethodField() + history_user_id = SerializerMethodField() + + def get_history_user(self, obj): # pylint: disable=R0201 + """ + Returns the username of the user who made the change. + """ + + return obj.history_user.username + + def get_history_user_id(self, obj): # pylint: disable=R0201 + """ + Returns the id of the user who made the change. + """ + + return obj.history_user.id + + class Meta: + model = HistoricalTerritorialEntity + fields = "__all__" diff --git a/project/api/urls.py b/project/api/urls.py index 0a490c29..c371fd8c 100644 --- a/project/api/urls.py +++ b/project/api/urls.py @@ -36,6 +36,8 @@ ROUTER.register(r"symbols", views.SymbolViewSet) ROUTER.register(r"symbol-features", views.SymbolFeatureViewSet) ROUTER.register(r"stv-history", views.StvHistoryViewSet) +ROUTER.register(r"te-history", views.TeHistoryViewSet) + urlpatterns = [ path( diff --git a/project/api/views/endpoints/__init__.py b/project/api/views/endpoints/__init__.py index fe81ce3d..80ad121b 100644 --- a/project/api/views/endpoints/__init__.py +++ b/project/api/views/endpoints/__init__.py @@ -4,4 +4,4 @@ from .mvt_cities import * from .mvt_narratives import * from .mvt_stv import * -from .stv_downloader import * \ No newline at end of file +from .stv_downloader import * diff --git a/project/api/views/views.py b/project/api/views/views.py index c9da6bd1..a8994cfa 100644 --- a/project/api/views/views.py +++ b/project/api/views/views.py @@ -36,7 +36,8 @@ Symbol, SymbolFeature, HistoricalSpacetimeVolume, -) + HistoricalTerritorialEntity, +) # pylint: disable=E0611 from api.serializers import ( TerritorialEntitySerializer, PoliticalRelationSerializer, @@ -51,6 +52,7 @@ SymbolFeatureSerializer, StvHistoryListSerializer, StvHistoryRetrieveSerializer, + TeHistorySerializer, ) from api.permissions import IsUserOrReadOnly @@ -210,36 +212,75 @@ class ProfileViewSet(viewsets.ModelViewSet): class HistoryPagination(LimitOffsetPagination): - page_size = 100 - page_size_query_param = 'limit' + """ + Pagination for history items + """ + + page_size_query_param = "limit" max_page_size = 1000 + + class StvHistoryViewSet(viewsets.ReadOnlyModelViewSet): """ ViewSet for SpacetimeVolume History """ + queryset = HistoricalSpacetimeVolume.objects.all() pagination_class = HistoryPagination - def get_queryset(self): queryset = self.queryset - if self.action == 'list': - stv = self.request.query_params.get('stv', None) - + if self.action == "list": + stv = self.request.query_params.get("stv", None) + if stv is not None: queryset = queryset.filter(id=stv) - te = self.request.query_params.get('te', None) + entity = self.request.query_params.get("entity", None) + + if entity is not None: + queryset = queryset.filter(entity=entity) + + user = self.request.query_params.get("user", None) + + if user is not None: + queryset = queryset.filter(history_user=user) - if te is not None: - queryset = queryset.filter(entity=te) - return queryset def get_serializer_class(self): - if self.action == 'list': - return StvHistoryListSerializer - if self.action == 'retrieve': - return StvHistoryRetrieveSerializer + if self.action == "list": + return StvHistoryListSerializer + if self.action == "retrieve": + return StvHistoryRetrieveSerializer + return StvHistoryListSerializer + + +class TeHistoryViewSet(viewsets.ReadOnlyModelViewSet): + """ + ViewSet for SpacetimeVolume History + """ + + queryset = HistoricalTerritorialEntity.objects.all() + pagination_class = HistoryPagination + serializer_class = TeHistorySerializer + + def get_queryset(self): + + queryset = self.queryset + + if self.action == "list": + + entity = self.request.query_params.get("entity", None) + + if entity is not None: + queryset = queryset.filter(id=entity) + + user = self.request.query_params.get("user", None) + + if user is not None: + queryset = queryset.filter(history_user=user) + + return queryset diff --git a/project/chron/settings.py b/project/chron/settings.py index 59bc4239..e91c8c1e 100644 --- a/project/chron/settings.py +++ b/project/chron/settings.py @@ -60,7 +60,7 @@ "drf_firebase_auth", "ordered_model", "silk", - "simple_history" + "simple_history", ] if DEBUG: @@ -79,7 +79,6 @@ # "django.middleware.clickjacking.XFrameOptionsMiddleware", "chron.middleware.ErrorMessageFormatter", "simple_history.middleware.HistoryRequestMiddleware", - ] if DEBUG: From 33eb4a95ab406ff20ec7345c5bf20c308f28c45d Mon Sep 17 00:00:00 2001 From: ata Date: Fri, 28 Feb 2020 17:44:58 +0300 Subject: [PATCH 06/14] history tests --- project/api/serializers.py | 31 ++++++++++++++++++++++++------- project/api/tests/stv_tests.py | 25 +++++++++++++++++++++++++ project/api/tests/te_tests.py | 31 +++++++++++++++++++++++++++++++ project/api/urls.py | 4 ++-- 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/project/api/serializers.py b/project/api/serializers.py index f7bb98bf..1624d261 100644 --- a/project/api/serializers.py +++ b/project/api/serializers.py @@ -269,14 +269,20 @@ def get_history_user(self, obj): # pylint: disable=R0201 Returns the username of the user who made the change. """ - return obj.history_user.username + if obj.history_user is not None: + return obj.history_user.username + else: + return None def get_history_user_id(self, obj): # pylint: disable=R0201 """ Returns the id of the user who made the change. """ - return obj.history_user.id + if obj.history_user is not None: + return obj.history_user.id + else: + return None class Meta: model = HistoricalSpacetimeVolume @@ -296,14 +302,20 @@ def get_history_user(self, obj): # pylint: disable=R0201 Returns the username of the user who made the change. """ - return obj.history_user.username + if obj.history_user is not None: + return obj.history_user.username + else: + return None def get_history_user_id(self, obj): # pylint: disable=R0201 """ Returns the id of the user who made the change. """ - return obj.history_user.id + if obj.history_user is not None: + return obj.history_user.id + else: + return None class Meta: model = HistoricalSpacetimeVolume @@ -323,14 +335,19 @@ def get_history_user(self, obj): # pylint: disable=R0201 Returns the username of the user who made the change. """ - return obj.history_user.username + if obj.history_user is not None: + return obj.history_user.username + else: + return None def get_history_user_id(self, obj): # pylint: disable=R0201 """ Returns the id of the user who made the change. """ - - return obj.history_user.id + if obj.history_user is not None: + return obj.history_user.id + else: + return None class Meta: model = HistoricalTerritorialEntity diff --git a/project/api/tests/stv_tests.py b/project/api/tests/stv_tests.py index f89c5e48..f6f72fc4 100644 --- a/project/api/tests/stv_tests.py +++ b/project/api/tests/stv_tests.py @@ -168,3 +168,28 @@ def test_api_can_not_create_stv(self): self.assertEqual(response.status_code, status.HTTP_201_CREATED) response = self.client.post(url, data_overlapping) self.assertEqual(response.status_code, status.HTTP_409_CONFLICT) + + @authorized + def test_api_can_query_stv_history(self): + """ + Ensure we can query for all Cities + """ + + url = reverse('stv-history-list') + response = self.client.get(url, format="json") + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data[0]["id"], 11) + + + + @authorized + def test_api_can_query_te_history_detail(self): + """ + Ensure we can query for all Cities + """ + + url = reverse('stv-history-detail', args=[11]) + response = self.client.get(url, format="json") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["id"], 11) \ No newline at end of file diff --git a/project/api/tests/te_tests.py b/project/api/tests/te_tests.py index 34c6755c..a309e15b 100644 --- a/project/api/tests/te_tests.py +++ b/project/api/tests/te_tests.py @@ -91,3 +91,34 @@ def test_api_can_query_te(self): response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["admin_level"], 1) + + + @authorized + def test_api_can_query_te_history(self): + """ + Ensure we can query for all Cities + """ + + url = reverse('te-history-list') + response = self.client.get(url, format="json") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data[0]["label"], "France") + + params = {'entity': '105'} + response = self.client.get(url, params, format="json") + + for record in response.data: + self.assertEqual(record["id"], 105) + + + + @authorized + def test_api_can_query_te_history_detail(self): + """ + Ensure we can query for all Cities + """ + + url = reverse('te-history-detail', args=[126]) + response = self.client.get(url, format="json") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["label"], "France") \ No newline at end of file diff --git a/project/api/urls.py b/project/api/urls.py index c371fd8c..d58a8ec6 100644 --- a/project/api/urls.py +++ b/project/api/urls.py @@ -35,8 +35,8 @@ ROUTER.register(r"profiles", views.ProfileViewSet) ROUTER.register(r"symbols", views.SymbolViewSet) ROUTER.register(r"symbol-features", views.SymbolFeatureViewSet) -ROUTER.register(r"stv-history", views.StvHistoryViewSet) -ROUTER.register(r"te-history", views.TeHistoryViewSet) +ROUTER.register(r"stv-history", views.StvHistoryViewSet, basename='stv-history') +ROUTER.register(r"te-history", views.TeHistoryViewSet, basename='te-history') urlpatterns = [ From c0fcd8ed1a36de37386e289449106573ab877b5f Mon Sep 17 00:00:00 2001 From: whirish Date: Sat, 14 Mar 2020 11:52:12 -0700 Subject: [PATCH 07/14] Add reversion mechanic --- project/.pylintrc | 3 ++- .../api/migrations/0022_auto_20200314_1805.py | 26 +++++++++++++++++++ project/api/models.py | 6 ++--- project/api/tests/stv_tests.py | 8 +++--- project/api/tests/te_tests.py | 11 +++----- project/api/urls.py | 4 +-- project/api/views/views.py | 22 +++++++++++----- 7 files changed, 55 insertions(+), 25 deletions(-) create mode 100644 project/api/migrations/0022_auto_20200314_1805.py diff --git a/project/.pylintrc b/project/.pylintrc index d4a615ef..455756d0 100644 --- a/project/.pylintrc +++ b/project/.pylintrc @@ -128,7 +128,8 @@ disable=parameter-unpacking, dict-values-not-iterating, fixme, bad-continuation, - no-member + no-member, + invalid-name, # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/project/api/migrations/0022_auto_20200314_1805.py b/project/api/migrations/0022_auto_20200314_1805.py new file mode 100644 index 00000000..a285a3e2 --- /dev/null +++ b/project/api/migrations/0022_auto_20200314_1805.py @@ -0,0 +1,26 @@ +# Generated by Django 2.2.9 on 2020-03-14 18:05 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0021_merge_20200213_1800'), + ] + + operations = [ + migrations.AddField( + model_name='historicalspacetimevolume', + name='history_relation', + field=models.ForeignKey(db_constraint=False, default=0, on_delete=django.db.models.deletion.DO_NOTHING, related_name='records', to='api.SpacetimeVolume'), + preserve_default=False, + ), + migrations.AddField( + model_name='historicalterritorialentity', + name='history_relation', + field=models.ForeignKey(db_constraint=False, default=9, on_delete=django.db.models.deletion.DO_NOTHING, related_name='records', to='api.TerritorialEntity'), + preserve_default=False, + ), + ] diff --git a/project/api/models.py b/project/api/models.py index 61c58d36..4eec74bb 100644 --- a/project/api/models.py +++ b/project/api/models.py @@ -104,7 +104,7 @@ class TerritorialEntity(models.Model): through="PoliticalRelation", related_name="political_relations", ) - history = HistoricalRecords() + history = HistoricalRecords(related_name="records") def clean(self, *args, **kwargs): # pylint: disable=W0221 if not self.inception_date is None and not self.dissolution_date is None: @@ -272,7 +272,7 @@ class SpacetimeVolume(models.Model): references = ArrayField(models.TextField(max_length=500), blank=True, null=True) visual_center = models.PointField(blank=True, null=True) related_events = models.ManyToManyField(CachedData, blank=True) - history = HistoricalRecords() + history = HistoricalRecords(related_name="records") def calculate_center(self): """ @@ -330,7 +330,7 @@ def clean(self, *args, **kwargs): # pylint: disable=W0221 super(SpacetimeVolume, self).clean(*args, **kwargs) def save(self, *args, **kwargs): # pylint: disable=W0221 - self.full_clean() + self.full_clean(exclude=["id"]) super(SpacetimeVolume, self).save(*args, **kwargs) if not self.visual_center: self.calculate_center() diff --git a/project/api/tests/stv_tests.py b/project/api/tests/stv_tests.py index f6f72fc4..9ac5294a 100644 --- a/project/api/tests/stv_tests.py +++ b/project/api/tests/stv_tests.py @@ -175,21 +175,19 @@ def test_api_can_query_stv_history(self): Ensure we can query for all Cities """ - url = reverse('stv-history-list') + url = reverse("stv-history-list") response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data[0]["id"], 11) - - @authorized def test_api_can_query_te_history_detail(self): """ Ensure we can query for all Cities """ - url = reverse('stv-history-detail', args=[11]) + url = reverse("stv-history-detail", args=[11]) response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data["id"], 11) \ No newline at end of file + self.assertEqual(response.data["id"], 11) diff --git a/project/api/tests/te_tests.py b/project/api/tests/te_tests.py index a309e15b..0936d117 100644 --- a/project/api/tests/te_tests.py +++ b/project/api/tests/te_tests.py @@ -92,33 +92,30 @@ def test_api_can_query_te(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["admin_level"], 1) - @authorized def test_api_can_query_te_history(self): """ Ensure we can query for all Cities """ - url = reverse('te-history-list') + url = reverse("te-history-list") response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data[0]["label"], "France") - params = {'entity': '105'} + params = {"entity": "105"} response = self.client.get(url, params, format="json") for record in response.data: self.assertEqual(record["id"], 105) - - @authorized def test_api_can_query_te_history_detail(self): """ Ensure we can query for all Cities """ - url = reverse('te-history-detail', args=[126]) + url = reverse("te-history-detail", args=[126]) response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data["label"], "France") \ No newline at end of file + self.assertEqual(response.data["label"], "France") diff --git a/project/api/urls.py b/project/api/urls.py index d58a8ec6..76174127 100644 --- a/project/api/urls.py +++ b/project/api/urls.py @@ -35,8 +35,8 @@ ROUTER.register(r"profiles", views.ProfileViewSet) ROUTER.register(r"symbols", views.SymbolViewSet) ROUTER.register(r"symbol-features", views.SymbolFeatureViewSet) -ROUTER.register(r"stv-history", views.StvHistoryViewSet, basename='stv-history') -ROUTER.register(r"te-history", views.TeHistoryViewSet, basename='te-history') +ROUTER.register(r"stv-history", views.StvHistoryViewSet, basename="stv-history") +ROUTER.register(r"te-history", views.TeHistoryViewSet, basename="te-history") urlpatterns = [ diff --git a/project/api/views/views.py b/project/api/views/views.py index a8994cfa..dd7551a8 100644 --- a/project/api/views/views.py +++ b/project/api/views/views.py @@ -18,6 +18,7 @@ """ from django.db.models import Count +from django.http import JsonResponse from rest_framework import viewsets, status from rest_framework.response import Response from rest_framework.permissions import IsAuthenticatedOrReadOnly @@ -229,22 +230,15 @@ class StvHistoryViewSet(viewsets.ReadOnlyModelViewSet): pagination_class = HistoryPagination def get_queryset(self): - queryset = self.queryset - if self.action == "list": stv = self.request.query_params.get("stv", None) - if stv is not None: queryset = queryset.filter(id=stv) - entity = self.request.query_params.get("entity", None) - if entity is not None: queryset = queryset.filter(entity=entity) - user = self.request.query_params.get("user", None) - if user is not None: queryset = queryset.filter(history_user=user) @@ -257,6 +251,13 @@ def get_serializer_class(self): return StvHistoryRetrieveSerializer return StvHistoryListSerializer + def update(self, request, pk=None): # pylint: disable=R0201 + """ + Reverts model to a certain HistoricalRecord on PUT + """ + HistoricalSpacetimeVolume.objects.get(history_id=pk).instance.save() + return JsonResponse({"status": "Model reverted successfully"}) + class TeHistoryViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -284,3 +285,10 @@ def get_queryset(self): queryset = queryset.filter(history_user=user) return queryset + + def update(self, request, pk=None): # pylint: disable=R0201 + """ + Reverts model to a certain HistoricalRecord on PUT + """ + HistoricalTerritorialEntity.objects.get(history_id=pk).instance.save() + return JsonResponse({"status": "Model reverted successfully"}) From 2eb87da0daf845b75ff19385044bfc9149de6e39 Mon Sep 17 00:00:00 2001 From: whirish Date: Sat, 14 Mar 2020 12:01:29 -0700 Subject: [PATCH 08/14] Remove reverse relation --- .../api/migrations/0022_auto_20200314_1805.py | 26 ------------------- project/api/models.py | 4 +-- 2 files changed, 2 insertions(+), 28 deletions(-) delete mode 100644 project/api/migrations/0022_auto_20200314_1805.py diff --git a/project/api/migrations/0022_auto_20200314_1805.py b/project/api/migrations/0022_auto_20200314_1805.py deleted file mode 100644 index a285a3e2..00000000 --- a/project/api/migrations/0022_auto_20200314_1805.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 2.2.9 on 2020-03-14 18:05 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0021_merge_20200213_1800'), - ] - - operations = [ - migrations.AddField( - model_name='historicalspacetimevolume', - name='history_relation', - field=models.ForeignKey(db_constraint=False, default=0, on_delete=django.db.models.deletion.DO_NOTHING, related_name='records', to='api.SpacetimeVolume'), - preserve_default=False, - ), - migrations.AddField( - model_name='historicalterritorialentity', - name='history_relation', - field=models.ForeignKey(db_constraint=False, default=9, on_delete=django.db.models.deletion.DO_NOTHING, related_name='records', to='api.TerritorialEntity'), - preserve_default=False, - ), - ] diff --git a/project/api/models.py b/project/api/models.py index 4eec74bb..27fb0551 100644 --- a/project/api/models.py +++ b/project/api/models.py @@ -104,7 +104,7 @@ class TerritorialEntity(models.Model): through="PoliticalRelation", related_name="political_relations", ) - history = HistoricalRecords(related_name="records") + history = HistoricalRecords() def clean(self, *args, **kwargs): # pylint: disable=W0221 if not self.inception_date is None and not self.dissolution_date is None: @@ -272,7 +272,7 @@ class SpacetimeVolume(models.Model): references = ArrayField(models.TextField(max_length=500), blank=True, null=True) visual_center = models.PointField(blank=True, null=True) related_events = models.ManyToManyField(CachedData, blank=True) - history = HistoricalRecords(related_name="records") + history = HistoricalRecords() def calculate_center(self): """ From f89113fc5c4515598a5f0cc65cb4b6800320157a Mon Sep 17 00:00:00 2001 From: whirish Date: Thu, 19 Mar 2020 14:08:28 -0700 Subject: [PATCH 09/14] Group STV changes for overlaps --- project/.pylintrc | 2 + project/api/apps.py | 18 +++++++++ .../0022_historicalspacetimevolume_group.py | 19 +++++++++ .../api/migrations/0023_auto_20200315_0628.py | 18 +++++++++ project/api/models.py | 26 ++++++++++++- project/api/serializers.py | 18 +++------ project/api/views/stv_view.py | 39 ++++++++++++++++--- 7 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 project/api/migrations/0022_historicalspacetimevolume_group.py create mode 100644 project/api/migrations/0023_auto_20200315_0628.py diff --git a/project/.pylintrc b/project/.pylintrc index 455756d0..3a59c369 100644 --- a/project/.pylintrc +++ b/project/.pylintrc @@ -130,6 +130,8 @@ disable=parameter-unpacking, bad-continuation, no-member, invalid-name, + too-many-locals, + no-name-in-module, # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/project/api/apps.py b/project/api/apps.py index cc685b22..6a4b42f6 100644 --- a/project/api/apps.py +++ b/project/api/apps.py @@ -18,6 +18,17 @@ """ from django.apps import AppConfig +from simple_history.signals import pre_create_historical_record + + +def add_invoice_to_historical_balance(sender, **kwargs): # pylint: disable=W0613 + """ + Save group to historical record + """ + instance = kwargs["instance"] + if getattr(instance, "group", None): + kwargs["history_instance"].group = instance.group + del instance.group class ApiConfig(AppConfig): @@ -26,3 +37,10 @@ class ApiConfig(AppConfig): """ name = "api" + + def ready(self): + from .models import HistoricalSpacetimeVolume # pylint: disable=C0415 + + pre_create_historical_record.connect( + add_invoice_to_historical_balance, sender=HistoricalSpacetimeVolume + ) diff --git a/project/api/migrations/0022_historicalspacetimevolume_group.py b/project/api/migrations/0022_historicalspacetimevolume_group.py new file mode 100644 index 00000000..cc48cacb --- /dev/null +++ b/project/api/migrations/0022_historicalspacetimevolume_group.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.9 on 2020-03-15 06:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0021_merge_20200213_1800'), + ] + + operations = [ + migrations.AddField( + model_name='historicalspacetimevolume', + name='group', + field=models.PositiveIntegerField(default=0), + preserve_default=False, + ), + ] diff --git a/project/api/migrations/0023_auto_20200315_0628.py b/project/api/migrations/0023_auto_20200315_0628.py new file mode 100644 index 00000000..1a6e9d35 --- /dev/null +++ b/project/api/migrations/0023_auto_20200315_0628.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.9 on 2020-03-15 06:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0022_historicalspacetimevolume_group'), + ] + + operations = [ + migrations.AlterField( + model_name='historicalspacetimevolume', + name='group', + field=models.PositiveIntegerField(blank=True, null=True), + ), + ] diff --git a/project/api/models.py b/project/api/models.py index 27fb0551..1d0438c8 100644 --- a/project/api/models.py +++ b/project/api/models.py @@ -258,6 +258,18 @@ def save(self, *args, **kwargs): # pylint: disable=W0221 super(City, self).save(*args, **kwargs) +class OverlapGroupedHistoricalModel(models.Model): + """ + Abstract model for history models in which + the territory changed due to an overlap + """ + + group = models.PositiveIntegerField(null=True, blank=True) + + class Meta: + abstract = True + + class SpacetimeVolume(models.Model): """ Maps a set of Territories to a TerritorialEntity at a specific time @@ -272,7 +284,7 @@ class SpacetimeVolume(models.Model): references = ArrayField(models.TextField(max_length=500), blank=True, null=True) visual_center = models.PointField(blank=True, null=True) related_events = models.ManyToManyField(CachedData, blank=True) - history = HistoricalRecords() + history = HistoricalRecords(bases=[OverlapGroupedHistoricalModel,]) def calculate_center(self): """ @@ -323,6 +335,7 @@ def clean(self, *args, **kwargs): # pylint: disable=W0221 self.territory.geom_type != "Polygon" and self.territory.geom_type != "MultiPolygon" ): + print(self.territory.geom_type) raise ValidationError( "Only Polygon and MultiPolygon objects are acceptable geometry types." ) @@ -335,6 +348,17 @@ def save(self, *args, **kwargs): # pylint: disable=W0221 if not self.visual_center: self.calculate_center() + def save_without_historical_record(self, *args, **kwargs): + """ + Saves the model without creating a new historical record + """ + self.skip_history_when_saving = True # pylint: disable=W0201 + try: + ret = self.save(*args, **kwargs) # pylint: disable=E1111 + finally: + del self.skip_history_when_saving + return ret + class Narrative(models.Model): """ diff --git a/project/api/serializers.py b/project/api/serializers.py index 1624d261..fd8a154f 100644 --- a/project/api/serializers.py +++ b/project/api/serializers.py @@ -271,8 +271,7 @@ def get_history_user(self, obj): # pylint: disable=R0201 if obj.history_user is not None: return obj.history_user.username - else: - return None + return None def get_history_user_id(self, obj): # pylint: disable=R0201 """ @@ -281,8 +280,7 @@ def get_history_user_id(self, obj): # pylint: disable=R0201 if obj.history_user is not None: return obj.history_user.id - else: - return None + return None class Meta: model = HistoricalSpacetimeVolume @@ -304,8 +302,7 @@ def get_history_user(self, obj): # pylint: disable=R0201 if obj.history_user is not None: return obj.history_user.username - else: - return None + return None def get_history_user_id(self, obj): # pylint: disable=R0201 """ @@ -314,8 +311,7 @@ def get_history_user_id(self, obj): # pylint: disable=R0201 if obj.history_user is not None: return obj.history_user.id - else: - return None + return None class Meta: model = HistoricalSpacetimeVolume @@ -337,8 +333,7 @@ def get_history_user(self, obj): # pylint: disable=R0201 if obj.history_user is not None: return obj.history_user.username - else: - return None + return None def get_history_user_id(self, obj): # pylint: disable=R0201 """ @@ -346,8 +341,7 @@ def get_history_user_id(self, obj): # pylint: disable=R0201 """ if obj.history_user is not None: return obj.history_user.id - else: - return None + return None class Meta: model = HistoricalTerritorialEntity diff --git a/project/api/views/stv_view.py b/project/api/views/stv_view.py index b172b58c..506ad41c 100644 --- a/project/api/views/stv_view.py +++ b/project/api/views/stv_view.py @@ -29,10 +29,12 @@ from django.core.files.uploadedfile import UploadedFile from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db import transaction, connection +from django.db.models import Max from django.http import JsonResponse -from rest_framework import viewsets +from rest_framework import status, viewsets +from rest_framework.response import Response -from api.models import SpacetimeVolume +from api.models import SpacetimeVolume, HistoricalSpacetimeVolume from api.serializers import SpacetimeVolumeSerializer @@ -175,7 +177,7 @@ def _overlaps_queryset(geom, start_date, end_date): ) -def _subtract_geometry(request, overlaps, geom): +def _subtract_geometry(request, overlaps, geom, gid): for entity, stvs in overlaps["db"].items(): overlaps[ "keep" if str(entity) not in request.POST.getlist("overlaps") else "modify" @@ -190,6 +192,7 @@ def _subtract_geometry(request, overlaps, geom): for overlap in overlaps["modify"]: overlap.territory = overlap.territory.difference(geom) + overlap.group = gid if _calculate_area(overlap.territory) < AREA_TOLERANCE: overlap.delete() else: @@ -216,6 +219,12 @@ def create(self, request, *args, **kwargs): Solve overlaps if included in request body """ + # Get next historical group id + gid_max = HistoricalSpacetimeVolume.objects.all().aggregate(Max("group"))[ + "group__max" + ] + gid_next = gid_max + 1 if gid_max else 1 + # Validate other data before overlaps empty_territory_data = request.data.copy() empty_territory_data["territory"] = "POINT (0 0)" @@ -255,9 +264,29 @@ def _overlaps(): if "overlaps" not in request.data and len(overlaps["db"]) > 0: return JsonResponse({"overlaps": overlaps["db"]}, status=409) - request.data["territory"] = _subtract_geometry(request, overlaps, geom) + request.data["territory"] = _subtract_geometry( + request, overlaps, geom, gid_next + ) - return super().create(request, *args, **kwargs) + # Group new record + new_serializer = self.get_serializer(data=request.data) + new_serializer.is_valid(raise_exception=True) + data = new_serializer.validated_data.copy() + events = data.pop("related_events", []) + + new_instance = SpacetimeVolume(**data) + if len(overlaps["db"]) > 0: + new_instance.group = gid_next + new_instance.save() + new_instance.related_events.set(events) + new_instance.save_without_historical_record() + + # Return response + return Response( + serializer.data, + status=status.HTTP_201_CREATED, + headers=self.get_success_headers(serializer.data), + ) def update(self, request, *args, **kwargs): """ From 47155d457ad848c22db42a17e35965475b2f3457 Mon Sep 17 00:00:00 2001 From: whirish Date: Thu, 19 Mar 2020 14:17:43 -0700 Subject: [PATCH 10/14] Remove debug info --- project/api/apps.py | 4 ++-- project/api/models.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/project/api/apps.py b/project/api/apps.py index 6a4b42f6..cd810664 100644 --- a/project/api/apps.py +++ b/project/api/apps.py @@ -21,7 +21,7 @@ from simple_history.signals import pre_create_historical_record -def add_invoice_to_historical_balance(sender, **kwargs): # pylint: disable=W0613 +def add_group_to_historical_record(sender, **kwargs): # pylint: disable=W0613 """ Save group to historical record """ @@ -42,5 +42,5 @@ def ready(self): from .models import HistoricalSpacetimeVolume # pylint: disable=C0415 pre_create_historical_record.connect( - add_invoice_to_historical_balance, sender=HistoricalSpacetimeVolume + add_group_to_historical_record, sender=HistoricalSpacetimeVolume ) diff --git a/project/api/models.py b/project/api/models.py index 1d0438c8..2b7dcec0 100644 --- a/project/api/models.py +++ b/project/api/models.py @@ -335,7 +335,6 @@ def clean(self, *args, **kwargs): # pylint: disable=W0221 self.territory.geom_type != "Polygon" and self.territory.geom_type != "MultiPolygon" ): - print(self.territory.geom_type) raise ValidationError( "Only Polygon and MultiPolygon objects are acceptable geometry types." ) From 6061237ee579b4f4a80090193cafcfff44bd2bde Mon Sep 17 00:00:00 2001 From: ata Date: Fri, 20 Mar 2020 16:36:52 +0300 Subject: [PATCH 11/14] historical stv downloads --- project/api/urls.py | 1 + project/api/views/endpoints/stv_downloader.py | 42 ++++++++++++++----- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/project/api/urls.py b/project/api/urls.py index 76174127..cae15b93 100644 --- a/project/api/urls.py +++ b/project/api/urls.py @@ -57,5 +57,6 @@ ), path("mvt/stv///", views.mvt_stv, name="mvt-stv"), path("spacetime-volumes//download", views.stv_downloader), + path("stv-history//download", views.historical_stv_downloader), path("", include(ROUTER.urls)), ] diff --git a/project/api/views/endpoints/stv_downloader.py b/project/api/views/endpoints/stv_downloader.py index a9e755ea..6165023f 100644 --- a/project/api/views/endpoints/stv_downloader.py +++ b/project/api/views/endpoints/stv_downloader.py @@ -22,18 +22,11 @@ from django.core.serializers import serialize from django.http import HttpResponse, JsonResponse from jdcal import jd2gcal -from api.models import SpacetimeVolume +from api.models import SpacetimeVolume, HistoricalSpacetimeVolume -def stv_downloader(request, primary_key): - """ - Download stvs as geojson. - """ - - stv = SpacetimeVolume.objects.filter(pk=primary_key) - if len(stv) == 0: - return HttpResponse(status=404) +def stv_to_geojson_response(stv): geojson = serialize( "geojson", stv, @@ -66,10 +59,39 @@ def stv_downloader(request, primary_key): end_date = jd2gcal(stv[0].end_date, 0) end_string = "{}-{}-{}".format(end_date[0], end_date[1], end_date[2]) + response = JsonResponse(geojson) response["Content-Disposition"] = "attachment;filename={}_{}_{}.json;".format( stv[0].entity.label, start_string, end_string ) - return response + +def stv_downloader(request, primary_key): + """ + Download stvs as geojson. + """ + + stv = SpacetimeVolume.objects.filter(pk=primary_key) + if len(stv) == 0: + return HttpResponse(status=404) + + return stv_to_geojson_response(stv) + + +def historical_stv_downloader(request, primary_key): + """ + Download historical stvs as geojson. + """ + + try: + history = HistoricalSpacetimeVolume.objects.get(history_id=primary_key) + except HistoricalSpacetimeVolume.DoesNotExist: + return HttpResponse(status=404) + + stv = SpacetimeVolume.objects.filter(id=history.id) + + if len(stv) == 0: + return HttpResponse(status=404) + + return stv_to_geojson_response(stv) From 6000da15dc647d31f63e51afbf911a63545ebef8 Mon Sep 17 00:00:00 2001 From: Michael Orlov Date: Mon, 30 Mar 2020 21:26:23 +0200 Subject: [PATCH 12/14] Merge migrations --- project/api/migrations/0024_merge_20200330_1854.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 project/api/migrations/0024_merge_20200330_1854.py diff --git a/project/api/migrations/0024_merge_20200330_1854.py b/project/api/migrations/0024_merge_20200330_1854.py new file mode 100644 index 00000000..277e37af --- /dev/null +++ b/project/api/migrations/0024_merge_20200330_1854.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.11 on 2020-03-30 18:54 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0023_auto_20200315_0628'), + ('api', '0023_mapcolorscheme'), + ] + + operations = [ + ] From 5c56448a0d8d50c8c430d826523445970b6f83dc Mon Sep 17 00:00:00 2001 From: ata Date: Fri, 3 Apr 2020 16:42:34 +0300 Subject: [PATCH 13/14] Actually fownload historical geojson --- project/api/views/endpoints/stv_downloader.py | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/project/api/views/endpoints/stv_downloader.py b/project/api/views/endpoints/stv_downloader.py index 6165023f..f9d469db 100644 --- a/project/api/views/endpoints/stv_downloader.py +++ b/project/api/views/endpoints/stv_downloader.py @@ -44,11 +44,12 @@ def stv_to_geojson_response(stv): geojson = json.loads(geojson) for features in geojson["features"]: - features["properties"]["visual_center"] = { - "type": "Feature", - "properties": None, - "geometry": json.loads(stv[0].visual_center.json), - } + if not stv[0].visual_center is None: + features["properties"]["visual_center"] = { + "type": "Feature", + "properties": None, + "geometry": json.loads(stv[0].visual_center.json), + } features["properties"]["entity"] = { "label": stv[0].entity.label, "pk": stv[0].entity.pk, @@ -85,13 +86,8 @@ def historical_stv_downloader(request, primary_key): """ try: - history = HistoricalSpacetimeVolume.objects.get(history_id=primary_key) - except HistoricalSpacetimeVolume.DoesNotExist: - return HttpResponse(status=404) - - stv = SpacetimeVolume.objects.filter(id=history.id) - - if len(stv) == 0: + history = HistoricalSpacetimeVolume.objects.filter(history_id=primary_key) + except len(history) == 0: return HttpResponse(status=404) - return stv_to_geojson_response(stv) + return stv_to_geojson_response(history) From 503d527352b8c4b2e386c5532917327717fba0ba Mon Sep 17 00:00:00 2001 From: ata Date: Fri, 3 Apr 2020 16:52:40 +0300 Subject: [PATCH 14/14] Lint --- project/api/views/endpoints/stv_downloader.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/project/api/views/endpoints/stv_downloader.py b/project/api/views/endpoints/stv_downloader.py index f9d469db..5e620ef9 100644 --- a/project/api/views/endpoints/stv_downloader.py +++ b/project/api/views/endpoints/stv_downloader.py @@ -25,8 +25,11 @@ from api.models import SpacetimeVolume, HistoricalSpacetimeVolume - def stv_to_geojson_response(stv): + """ + Function for serializing stv queryset with one member to geojson. + """ + geojson = serialize( "geojson", stv, @@ -68,6 +71,7 @@ def stv_to_geojson_response(stv): ) return response + def stv_downloader(request, primary_key): """ Download stvs as geojson. @@ -85,9 +89,8 @@ def historical_stv_downloader(request, primary_key): Download historical stvs as geojson. """ - try: - history = HistoricalSpacetimeVolume.objects.filter(history_id=primary_key) - except len(history) == 0: + history = HistoricalSpacetimeVolume.objects.filter(history_id=primary_key) + if len(history) == 0: return HttpResponse(status=404) return stv_to_geojson_response(history)