diff options
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") | 
