summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2025-05-09 10:42:44 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2025-06-13 18:16:05 +0200
commitd829d11aec86a015d3898738ae9d73b5067bb865 (patch)
tree699c21e561d4335880985f39ccb68ecb678d2560
parent5b0c5f805eb444269668e318082ec7809719ff8f (diff)
downloadIshtar-d829d11aec86a015d3898738ae9d73b5067bb865.tar.bz2
Ishtar-d829d11aec86a015d3898738ae9d73b5067bb865.zip
✨ GIS API: get data sources list
-rw-r--r--ishtar_common/admin.py4
-rw-r--r--ishtar_common/models_imports.py28
-rw-r--r--ishtar_common/rest.py42
-rw-r--r--ishtar_common/templates/ishtar/gis_token_list.html7
-rw-r--r--ishtar_common/urls.py7
-rw-r--r--ishtar_common/views.py2
-rw-r--r--ishtar_common/views_api.py51
7 files changed, 133 insertions, 8 deletions
diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py
index f3c59f144..2ae7ee732 100644
--- a/ishtar_common/admin.py
+++ b/ishtar_common/admin.py
@@ -2151,13 +2151,13 @@ serialize_importer_action.short_description = SERIALIZE_DESC
@admin.register(models.ImporterType, site=admin_site)
class ImporterTypeAdmin(ImportJSONActionAdmin):
- list_display = ("name", "associated_models", "available", "importer_groups_label")
+ list_display = ("name", "associated_models", "type", "available", "importer_groups_label")
actions = importer_type_actions + [
serialize_importer_action,
change_value("available", True, _("Make available")),
change_value("available", False, _("Make unavailable")),
]
- list_filter = ["available"]
+ list_filter = ["available", "type"]
search_fields = ["name"]
autocomplete_fields = ["users"]
prepopulated_fields = {"slug": ("name",)}
diff --git a/ishtar_common/models_imports.py b/ishtar_common/models_imports.py
index 63a5b92fc..33d793333 100644
--- a/ishtar_common/models_imports.py
+++ b/ishtar_common/models_imports.py
@@ -144,6 +144,7 @@ class ImporterModel(models.Model):
IMPORT_TYPES = (
("tab", _("Table")),
("gis", _("GIS")),
+ ("qgs", _("QGIS")),
)
IMPORT_TYPES_DICT = dict(IMPORT_TYPES)
@@ -222,6 +223,14 @@ class ImporterType(models.Model):
verbose_name = _("Importer - Type")
verbose_name_plural = _("Importer - Types")
ordering = ("name",)
+ # own is defined by users
+ # change permission need view permission
+ permissions = (
+ ("view_gis_importer", "Can export to QGIS"),
+ ("view_own_gis_importer", "Can export own to QGIS"),
+ ("change_gis_importer", "Can import from QGIS"),
+ ("change_own_gis_importer", "Can import own to QGIS"),
+ )
ADMIN_SECTION = _("Imports")
def natural_key(self):
@@ -230,10 +239,29 @@ class ImporterType(models.Model):
def __str__(self):
return self.name
+ def clean(self):
+ if self.type == "qgs" and not self.is_template:
+ raise ValidationError(
+ "QGIS importers should at least can be exported. Check the \"Can be "
+ "exported\" case.")
+
@classmethod
def is_own(cls, ishtar_user):
return bool(cls.objects.filter(users__pk=ishtar_user.pk).count())
+ @classmethod
+ def q_qgis_importers(cls, ishtaruser):
+ """
+ Return QGIS importer query
+ """
+ # filter available according to permissions
+ q = cls.objects.filter(type="qgs", is_template=True, available=True)
+ if not ishtaruser.is_superuser and not ishtaruser.has_permission("change_gis_importer"):
+ if not ishtaruser.has_permission("change_own_gis_importer"):
+ return q.filter(pk__isnull=True)
+ q = q.filter(users__pk=ishtaruser.pk)
+ return q
+
@property
def type_label(self):
if self.type in IMPORT_TYPES_DICT:
diff --git a/ishtar_common/rest.py b/ishtar_common/rest.py
index 699440f15..3d0c90f04 100644
--- a/ishtar_common/rest.py
+++ b/ishtar_common/rest.py
@@ -1,14 +1,36 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright (C) 2021-2025 Étienne Loks <etienne.loks at iggdrasil dot net>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero 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 Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# See the file COPYING for details.
+
+import datetime
+
from django.conf import settings
from django.db.models import Q
from django.http import HttpResponse
from django.utils.translation import activate, deactivate
-from rest_framework import authentication, permissions, generics
+from rest_framework import authentication, exceptions, permissions, generics
from rest_framework.response import Response
from rest_framework.views import APIView
from ishtar_common import models_rest
from ishtar_common.models_common import GeneralType
+from ishtar_common.utils import gettext_lazy as _
from ishtar_common.views_item import get_item
@@ -253,3 +275,21 @@ class GetAPIView(generics.RetrieveAPIView):
obj = q.all()[0]
result = obj.full_serialize(search_model, request=request)
return Response(result, content_type="json")
+
+
+class GISAuthentication(authentication.TokenAuthentication):
+ model = models_rest.UserToken
+
+ def authenticate_credentials(self, key):
+ """
+ Manage date limit
+ """
+ __, token = super().authenticate_credentials(key)
+ if token.limit_date and token.limit_date < datetime.date.today():
+ raise exceptions.AuthenticationFailed(_("Access expired."))
+ return (token.user, token)
+
+
+class GISAPIView(APIView):
+ authentication_classes = (GISAuthentication,)
+ permission_classes = (permissions.IsAuthenticated,)
diff --git a/ishtar_common/templates/ishtar/gis_token_list.html b/ishtar_common/templates/ishtar/gis_token_list.html
index ca4389076..ee4efcd19 100644
--- a/ishtar_common/templates/ishtar/gis_token_list.html
+++ b/ishtar_common/templates/ishtar/gis_token_list.html
@@ -45,9 +45,10 @@
{% with field=request_form.access_type %}
{% include "blocks/bs_field_snippet.html" %}
{% endwith %}
- {% with field=request_form.comment %}
+ {% with field=request_form.name %}
{% include "blocks/bs_field_snippet.html" %}
{% endwith %}
+ <div class="col-lg-6"></div>
{% with field=request_form.limit_date %}
{% include "blocks/bs_field_snippet.html" %}
{% endwith %}
@@ -66,14 +67,14 @@
<table class="table table-striped w-50">
<tr>
<th>{% trans "Access type (limit date)" %}</th>
- <th>{% trans "Comment" %}</th>
+ <th>{% trans "Name" %}</th>
<th>{% trans "Last access (IP)" %}</th>
<th>{% trans "Delete" %}</th>
</tr>
{% for access in object_list %}
<tr>
<td>{{access.access_type_label}}{% if access.limit_date %} ({{access.limit_date|date:"DATE_FORMAT"|default:"-"}}){% endif %}</td>
- <td>{{access.comment|default:"-"}}</td>
+ <td>{{access.name|default:"-"}}</td>
<td>{{access.last_access|date:"DATE_FORMAT"}} ({{access.last_ip|default:"-"}})</td>
<td>
<a class="btn btn-danger btn-sm" title="{% trans 'Delete' %}" href="{% url 'gis-token-delete' access.key %}"
diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py
index bd24d9c04..31d3b40ad 100644
--- a/ishtar_common/urls.py
+++ b/ishtar_common/urls.py
@@ -25,7 +25,7 @@ from django.views.generic import TemplateView
from .menus import Menu
-from ishtar_common import views, models, views_item
+from ishtar_common import models, views, views_item, views_api
from ishtar_common.utils import check_permissions, get_urls_for_model
# be careful: each check_permissions must be relevant with ishtar_menu
@@ -366,6 +366,11 @@ urlpatterns = [
views.gis_create_token,
name="gis-token-create",
),
+ path(
+ "api/gis/sources/",
+ views_api.GISSourceAPI.as_view(),
+ name="api-gis-sources",
+ ),
re_path(r"^profile(?:/(?P<pk>[0-9]+))?/$", views.ProfileEdit.as_view(), name="profile"),
re_path(
r"^save-search/(?P<app_label>[a-z-]+)/(?P<model>[a-z-]+)/$",
diff --git a/ishtar_common/views.py b/ishtar_common/views.py
index b52b837fa..cc733f684 100644
--- a/ishtar_common/views.py
+++ b/ishtar_common/views.py
@@ -1597,7 +1597,7 @@ def gis_create_token(request, request_key, app_key):
return HttpResponse(content_type="text/plain")
client_ip, __ = get_client_ip(request)
token = q.all()[0].generate_token(app_key, from_ip=client_ip)
- return HttpResponse((token and token.key) or "", content_type="text/plain")
+ return HttpResponse((token and token.key[7:]) or "", content_type="text/plain")
class DynamicModelView:
diff --git a/ishtar_common/views_api.py b/ishtar_common/views_api.py
new file mode 100644
index 000000000..c054246e4
--- /dev/null
+++ b/ishtar_common/views_api.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright (C) 2025 Étienne Loks <etienne.loks at iggdrasil dot net>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero 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 Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# See the file COPYING for details.
+
+from rest_framework import serializers
+from rest_framework.response import Response
+
+from ishtar_common.rest import GISAPIView
+from ishtar_common.models_imports import ImporterType
+
+
+class ImporterTypeSerializer(serializers.ModelSerializer):
+ model = serializers.SerializerMethodField()
+
+ class Meta:
+ model = ImporterType
+ fields = ['slug', 'name', 'description', 'is_import', 'is_template', 'model']
+
+ def get_model(self, obj):
+ if not obj.associated_models:
+ return ""
+ return str(obj.associated_models)
+
+
+class GISSourceAPI(GISAPIView):
+ model = ImporterType
+
+ def get_queryset(self):
+ user = self.request.user
+ if not user.ishtaruser:
+ return self.model.filter(pk__isnull=True)
+ return self.model.q_qgis_importers(user.ishtaruser)
+
+ def get(self, request, format=None):
+ serializer = ImporterTypeSerializer(self.get_queryset(), many=True)
+ return Response(serializer.data)