diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-11-15 16:37:42 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2022-12-12 12:20:59 +0100 |
commit | 45f3650ba29224ea849a6784ca04731bad29f745 (patch) | |
tree | edb1258d180c35e73a10ab7120c69d81b3925d44 /ishtar_common | |
parent | 160015547eb6173a50e396864ae5b55b3599d0a0 (diff) | |
download | Ishtar-45f3650ba29224ea849a6784ca04731bad29f745.tar.bz2 Ishtar-45f3650ba29224ea849a6784ca04731bad29f745.zip |
Syndication - serialization - display sheet operation
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/models.py | 3 | ||||
-rw-r--r-- | ishtar_common/models_common.py | 49 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/blocks/window_field_flex_multiple.html | 6 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/blocks/window_nav.html | 6 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/sheet.html | 8 | ||||
-rw-r--r-- | ishtar_common/templatetags/ishtar_helpers.py | 14 | ||||
-rw-r--r-- | ishtar_common/templatetags/window_field.py | 12 | ||||
-rw-r--r-- | ishtar_common/utils.py | 13 | ||||
-rw-r--r-- | ishtar_common/views_item.py | 83 |
9 files changed, 164 insertions, 30 deletions
diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 59248fa43..442e67429 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -3619,7 +3619,7 @@ class Document( _TABLE_COLS = [ "title", - "source_type", + "source_type__label", "cache_related_label", "authors__cached_label", "associated_url", @@ -3658,6 +3658,7 @@ class Document( COL_LABELS = { "authors__cached_label": _("Authors"), "complete_identifier": _("Identifier"), + "source_type__label": _("Type") } CACHED_LABELS = ["cache_related_label"] diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index 58eac91e2..e55a21e0c 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -9,6 +9,7 @@ import copy from collections import OrderedDict import datetime import json +import locale import logging import os import pyqrcode @@ -39,7 +40,8 @@ from django.db.models.signals import post_save, post_delete, m2m_changed from django.template.defaultfilters import slugify from django.utils.safestring import SafeText, mark_safe from django.utils.translation import activate, deactivate -from ishtar_common.utils import ugettext_lazy as _, pgettext_lazy, get_image_path +from ishtar_common.utils import ugettext_lazy as _, pgettext_lazy, get_image_path, \ + human_date from simple_history.models import HistoricalRecords as BaseHistoricalRecords from simple_history.signals import ( post_create_historical_record, @@ -2536,6 +2538,10 @@ class DocumentItem: ), } + def documents_list(self) -> list: + Document = apps.get_model("ishtar_common", "Document") + return self.get_associated_main_item_list("documents", Document) + def public_representation(self): images = [] if getattr(self, "main_image", None): @@ -3247,6 +3253,9 @@ class MainItem(ShortMenuItem): QUICK_ACTIONS = [] SERIALIZE_EXCLUDE = ["search_vector"] + SERIALIZE_PROPERTIES = ["external_id", "multi_polygon_geojson", "point_2d_geojson"] + SERIALIZE_CALL = {} + SERIALIZE_DATES = [] def full_serialize(self) -> dict: """ @@ -3268,20 +3277,56 @@ class MainItem(ShortMenuItem): elif field.many_to_many: values = getattr(self, field.name) if values.count(): - values = [str(v) for v in values] + values = [str(v) for v in values.all()] else: values = [] full_result[field.name] = values else: serialize_fields.append(field.name) + result = json.loads(serialize( "json", [self], fields=serialize_fields )) full_result.update(result[0]["fields"]) + for prop in self.SERIALIZE_PROPERTIES: + if prop not in full_result: + full_result[prop] = getattr(self, prop) + if "point_2d_geojson" in full_result: + full_result["point_2d"] = True + if "multi_polygon_geojson" in full_result: + full_result["multi_polygon"] = True + for prop in self.SERIALIZE_DATES: + dt = getattr(self, prop) or "" + if dt: + dt = human_date(dt) + full_result[prop] = dt + for k in self.SERIALIZE_CALL: + full_result[k] = getattr(self, self.SERIALIZE_CALL[k])() + full_result["SLUG"] = self.SLUG + full_result["pk"] = f"external_{self.pk}" + full_result["id"] = f"external_{self.id}" return full_result + def get_associated_main_item_list(self, attr, model) -> list: + items = getattr(self, attr) + if not items.count(): + return [] + lst = [] + table_cols = model.TABLE_COLS + if callable(table_cols): + table_cols = table_cols() + for colname in table_cols: + if colname in model.COL_LABELS: + lst.append(str(model.COL_LABELS[colname])) + else: + lst.append(model._meta.get_field(colname).verbose_name) + lst = [lst] + for values in items.values_list(*table_cols): + lst.append(["-" if v is None else v for v in values]) + return lst + @classmethod def class_verbose_name(cls): return cls._meta.verbose_name diff --git a/ishtar_common/templates/ishtar/blocks/window_field_flex_multiple.html b/ishtar_common/templates/ishtar/blocks/window_field_flex_multiple.html index abf03ead1..1587963c5 100644 --- a/ishtar_common/templates/ishtar/blocks/window_field_flex_multiple.html +++ b/ishtar_common/templates/ishtar/blocks/window_field_flex_multiple.html @@ -1,8 +1,8 @@ -{% load i18n %}{% if data.count %} +{% load i18n %}{% if data.count or external %} <dl class="col-12 {% if size == 2 %}col-lg-6{% else %}col-md-6 col-lg-3{% endif %} flex-wrap"> <dt>{% trans caption %}</dt> - <dd>{% for d in data.all %} + <dd>{% if not external %}{% for d in data.all %} {% if forloop.counter0 %} ; {% endif %}{{ d }} - {% endfor %}</dd> + {% endfor %}{% else %}{{data}}{% endif %}</dd> </dl> {% endif %} diff --git a/ishtar_common/templates/ishtar/blocks/window_nav.html b/ishtar_common/templates/ishtar/blocks/window_nav.html index 1d8121faf..a1ec01dcb 100644 --- a/ishtar_common/templates/ishtar/blocks/window_nav.html +++ b/ishtar_common/templates/ishtar/blocks/window_nav.html @@ -1,4 +1,5 @@ {% load i18n ishtar_helpers %} +{% if not item.is_external %} <div class="row toolbar"> {% if current_user.is_superuser %} {% if previous or next %} @@ -112,4 +113,7 @@ $(document).ready(function(){ register_qa_on_sheet(); }); -</script>
\ No newline at end of file +</script> +{% else %} +<h3><i class="fa fa-globe" aria-hidden="true"></i> {{item.current_source}}</h3> +{% endif %}
\ No newline at end of file diff --git a/ishtar_common/templates/ishtar/sheet.html b/ishtar_common/templates/ishtar/sheet.html index b0f82941f..4596d8ec6 100644 --- a/ishtar_common/templates/ishtar/sheet.html +++ b/ishtar_common/templates/ishtar/sheet.html @@ -79,19 +79,19 @@ var last_window='{{window_id}}'; jQuery(document).ready(function(){ - if (! get_next_table_id({{item.pk}})){ + if (! get_next_table_id("{{item.pk}}")){ jQuery('.next_page').hide(); } - if (! get_previous_table_id({{item.pk}})){ + if (! get_previous_table_id("{{item.pk}}")){ jQuery('.previous_page').hide(); } jQuery(".next_page").click(function() { - load_window("{{current_window_url}}" + get_next_table_id({{item.pk}}) + "/", + load_window("{{current_window_url}}" + get_next_table_id("{{item.pk}}") + "/", '', function(){hide_window("{{window_id}}");}, true); }); jQuery(".previous_page").click(function() { - load_window("{{current_window_url}}" + get_previous_table_id({{item.pk}}) + "/", + load_window("{{current_window_url}}" + get_previous_table_id("{{item.pk}}") + "/", '', function(){hide_window("{{window_id}}");}, true); }); diff --git a/ishtar_common/templatetags/ishtar_helpers.py b/ishtar_common/templatetags/ishtar_helpers.py index ade89bdc0..250b5719d 100644 --- a/ishtar_common/templatetags/ishtar_helpers.py +++ b/ishtar_common/templatetags/ishtar_helpers.py @@ -25,10 +25,16 @@ def safe_or(item, args): result = True current_item = item for sub in arg.split("."): - if not hasattr(current_item, sub) or not getattr(current_item, sub): - result = False - break - current_item = getattr(current_item, sub) + if isinstance(current_item, dict): + if sub not in current_item: + result = False + break + current_item = current_item[sub] + else: + if not hasattr(current_item, sub) or not getattr(current_item, sub): + result = False + break + current_item = getattr(current_item, sub) if result: return True diff --git a/ishtar_common/templatetags/window_field.py b/ishtar_common/templatetags/window_field.py index b21f2b9e6..cffa2a54f 100644 --- a/ishtar_common/templatetags/window_field.py +++ b/ishtar_common/templatetags/window_field.py @@ -98,12 +98,20 @@ def field_multiple(caption, data, li=False, size=None): @register.simple_tag def field_multiple_obj(caption, item, attr, li=False, size=None): - data = getattr(item, attr) if hasattr(item, attr) else "" + data, external = "", False + if isinstance(item, dict): + if attr in item: + data = " ; ".join(item[attr]) + if data: + external = True + else: + data = getattr(item, attr) if hasattr(item, attr) else "" if not hasattr(item, '_step') or attr not in item.history_m2m \ or not item.history_m2m[attr]: t = loader.get_template('ishtar/blocks/window_field_flex_multiple.html') return t.render( - {'caption': caption, 'data': data, 'li': li, "size": size} + {'caption': caption, 'data': data, 'li': li, "size": size, + "external": external} ) field = getattr(item.instance.__class__, attr) if hasattr(field, "remote_field"): diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index 3a349c04b..80d5af9d6 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -27,6 +27,7 @@ import hashlib from importlib import import_module import io from jinja2 import Template +import locale import os import random import re @@ -2111,6 +2112,18 @@ def get_image_path(instance, filename): return instance._get_image_path(filename) +def human_date(value): + language_code = settings.LANGUAGE_CODE.split("-") + language_code = language_code[0] + "_" + language_code[1].upper() + for language_suffix in (".utf8", ""): + try: + locale.setlocale(locale.LC_TIME, language_code + language_suffix) + break + except locale.Error: + pass + return value.strftime(settings.DATE_FORMAT) + + class IshtarFileSystemStorage(FileSystemStorage): def exists(self, name): path_name = self.path(name) diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 7ddfa179a..468312046 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -33,7 +33,7 @@ from django.db.models.functions import ExtractYear from django.db.utils import ProgrammingError from django import forms from django.forms.models import model_to_dict -from django.http import HttpResponse +from django.http import HttpResponse, Http404 from django.shortcuts import render from django.template import loader from django.urls import reverse, NoReverseMatch @@ -283,6 +283,52 @@ def display_item(model, extra_dct=None, show_url=None): return func +def show_source_item(request, source_id, model, name, base_dct, extra_dct): + try: + __, source_id, external_id = source_id.split("-") + source_id, external_id = int(source_id), int(external_id) + except ValueError: + raise Http404() + models_rest.ApiExternalSource.objects.get() + # TODO: check permissions + try: + src = models_rest.ApiExternalSource.objects.get( + pk=source_id) + except models_rest.ApiExternalSource.DoesNotExist: + return HttpResponse("{}", content_type="text/plain") + url = src.url + if not url.endswith("/"): + url += "/" + url += f"api/get/{model.SLUG}/{external_id}/" + try: + response = requests.get( + url, + timeout=20, + headers={"Authorization": f"Token {src.key}"}, + ) + except requests.exceptions.Timeout: + return HttpResponse("{}", content_type="text/plain") + item = response.json() + dct = deepcopy(base_dct) + if not item: + return HttpResponse("{}", content_type="text/plain") + item["is_external"] = True + item["current_source"] = src.name + dct["item"], dct["item_name"] = item, name + dct["is_external"] = True + + if extra_dct: + dct.update(extra_dct(request, dct)) + + permissions = ["permission_view_document"] + for p in permissions: + dct[p] = True + + tpl = loader.get_template(f"ishtar/sheet_{name}_window.html") + content = tpl.render(dct, request) + return HttpResponse(content, content_type="application/xhtml") + + def show_item(model, name, extra_dct=None, model_for_perms=None): def func(request, pk, **dct): check_model = model @@ -298,10 +344,6 @@ def show_item(model, name, extra_dct=None, model_for_perms=None): query_own = model.get_query_owns(request.user.ishtaruser) if query_own: q = q.filter(query_own).distinct() - try: - item = q.get(pk=pk) - except (ObjectDoesNotExist, ValueError): - return HttpResponse("") doc_type = "type" in dct and dct.pop("type") url_name = ( "/".join(reverse("show-" + name, args=["0", ""]).split("/")[:-2]) + "/" @@ -315,12 +357,15 @@ def show_item(model, name, extra_dct=None, model_for_perms=None): date = None if "date" in dct: date = dct.pop("date") - dct["sheet_id"] = "%s-%d" % (name, item.pk) - dct["window_id"] = "%s-%d-%s" % ( - name, - item.pk, - datetime.datetime.now().strftime("%M%s"), - ) + dct["sheet_id"] = f"{name}-{pk}" + dct["window_id"] = f"{name}-{pk}-{datetime.datetime.now().strftime('%M%s')}" + if pk.startswith("source-"): + return show_source_item( + request, pk, model, name, dct, extra_dct) + try: + item = q.get(pk=pk) + except (ObjectDoesNotExist, ValueError): + return HttpResponse("") # list current perms if hasattr(request.user, "ishtaruser") and request.user.ishtaruser: @@ -2359,7 +2404,7 @@ def get_distant_item( try: src = models_rest.ApiExternalSource.objects.get( pk=external_source_id) - except models_rest.ApiExternalSource.DoesNotExist: + except (models_rest.ApiExternalSource.DoesNotExist, ValueError): return HttpResponse("{}", content_type="text/plain") url = src.url url += reverse(f"api-search-{model}") @@ -2377,6 +2422,18 @@ def get_distant_item( ) except requests.exceptions.Timeout: return HttpResponse("{}", content_type="text/plain") - return HttpResponse(json.dumps(response.json()), content_type="application/json") + dct = response.json() + if "rows" in dct: + for row in dct["rows"]: + if "id" in row: + try: + idx = int(row['id']) + except ValueError: + continue + source_id = f"source-{external_source_id}-{idx}" + row["id"] = source_id + if "link" in row: + row["link"] = row["link"].replace(str(idx), source_id) + return HttpResponse(json.dumps(dct), content_type="application/json") |