diff options
-rw-r--r-- | archaeological_operations/models.py | 20 | ||||
-rw-r--r-- | archaeological_operations/templates/ishtar/sheet_operation.html | 83 | ||||
-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 |
11 files changed, 256 insertions, 41 deletions
diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index 050e50240..4747fbf9e 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -75,6 +75,7 @@ from ishtar_common.model_managers import UUIDModelManager from ishtar_common.utils import ( cached_label_changed, force_cached_label_changed, + human_date, mode, m2m_historization_changed, post_save_geo, @@ -732,6 +733,14 @@ def get_values_town_related(item, prefix, values, filtr=None): class ClosedItem(object): + def serialize_closing(self): + value = self.closing() + if not value: + return "" + value["date"] = human_date(value["date"]) + value["user"] = str(value["user"]) + return value + def closing(self): if self.is_active(): return @@ -1120,6 +1129,11 @@ class Operation( "towns", "periods", ] + SERIALIZE_PROPERTIES = MainItem.SERIALIZE_PROPERTIES + ["short_label"] + SERIALIZE_DATES = ["start_date", "excavation_end_date"] + SERIALIZE_CALL = {"closing": "serialize_closing", + "archaeological_sites_list": "archaeological_sites_list", + "documents_list": "documents_list"} # fields definition uuid = models.UUIDField(default=uuid.uuid4) @@ -1460,6 +1474,10 @@ class Operation( ) return dct + def archaeological_sites_list(self) -> list: + return self.get_associated_main_item_list("archaeological_sites", + ArchaeologicalSite) + @classmethod def _get_department_code(cls, value): if not settings.ISHTAR_DPTS: @@ -1499,7 +1517,7 @@ class Operation( return [town.label_with_areas for town in self.towns.all()] def towns_label(self): - return " - ".join(self.towns_codes()) + return " ; ".join(self.towns_codes()) def has_finds(self): from archaeological_finds.models import BaseFind diff --git a/archaeological_operations/templates/ishtar/sheet_operation.html b/archaeological_operations/templates/ishtar/sheet_operation.html index babcc9ce0..811997515 100644 --- a/archaeological_operations/templates/ishtar/sheet_operation.html +++ b/archaeological_operations/templates/ishtar/sheet_operation.html @@ -21,9 +21,10 @@ {% with display_data=item.data %} {% with display_relations=item|safe_or:"right_relations.count|left_relations.count" %} -{% with display_sites=item|safe_or:"archaeological_sites.count|grouped_parcels|administrative_act.count" %} +{% with display_sites=item|safe_or:"archaeological_sites.count|grouped_parcels|administrative_act.count|archaeological_sites_list" %} {% with perm_documents=permission_view_own_document|or_:permission_view_document %} -{% with display_documents=perm_documents|and_:item.documents.count %} +{% with has_documents=item|safe_or:"documents.count|documents_list" %} +{% with display_documents=perm_documents|and_:has_documents %} {% with perm_context_records=permission_view_own_contextrecord|or_:permission_view_contextrecord %} {% with has_context_records=item|safe_or:"context_record.count" %} {% with display_context_records=perm_context_records|and_:has_context_records %} @@ -31,7 +32,7 @@ {% with has_finds=item|safe_or:"has_finds" %} {% with display_finds=perm_find|and_:has_finds %} -{% if output != "ODT" and output != "PDF"%} +{% if output != "ODT" and output != "PDF" %} <ul class="nav nav-tabs" id="{{window_id}}-tabs" role="tablist"> <li class="nav-item"> <a class="nav-link active" id="{{window_id}}-general-tab" @@ -94,6 +95,7 @@ </a> </li> {% endif %} + {% if not is_external %} <li class="nav-item"> <a class="nav-link" id="{{window_id}}-statistics-tab" data-toggle="tab" href="#{{window_id}}-statistics" role="tab" @@ -101,6 +103,7 @@ {% trans "Statistics" %} </a> </li> + {% endif %} </ul> {% endif %} @@ -131,7 +134,7 @@ {% if next %} {{ item|m2m_listing:'towns'|join:" ; "|default:'' }} {% else %} - {{ item.towns_codes|join:" ; "|default:'' }} + {{ item.cached_towns_label }} {% endif %} </p> <p class='window-refs' title="{% trans 'Name' %}">{{item.common_name|default:''}}</p> @@ -141,13 +144,19 @@ <div class="row"> {% trans "Excavation dates (start/end)" as date_label %} + {% if not is_external %} {% with start_date=item.start_date|date:"DATE_FORMAT"|default:"-" %} {% with end_date=item.excavation_end_date|date:"DATE_FORMAT"|default:"-" %} {% with dates=start_date|add:" / "|add:end_date %} {% field_flex_2 date_label dates %} - {% endwith %} - {% endwith %} - {% endwith %} + {% endwith %}{% endwith %}{% endwith %} + {% else %} + {% with start_date=item.start_date|default:"-" %} + {% with end_date=item.excavation_end_date|default:"-" %} + {% with dates=start_date|add:" / "|add:end_date %} + {% field_flex_2 date_label dates %} + {% endwith %}{% endwith %}{% endwith %} + {% endif %} <dl class="col-12 col-md-6 col-lg-3 flex-wrap"> <dt> {% trans "State" %} @@ -243,10 +252,12 @@ {% field_flex_full "Comment about scientific documentation" item.scientific_documentation_comment "<pre>" "</pre>" %} </div> + {% if not is_external %} <h3>{% trans "Sheet"%}</h3> <div class="row"> {% include "ishtar/blocks/sheet_creation_section.html" %} </div> + {% endif %} {% if item.virtual_operation %} <div class="alert alert-warning" role="alert"> @@ -282,7 +293,7 @@ {% if next %} {% field_flex_full "Towns" item|m2m_listing:'towns'|join:" ; " %} {% else %} - {% field_flex_full "Towns" item.towns_codes|join:" ; " %} + {{ item.cached_towns_label }} {% endif %} {% field_flex "Address" item.address %} {% if not item.address %} @@ -301,9 +312,24 @@ {% if display_sites %} <div class="tab-pane fade" id="{{window_id}}-sites" role="tabpanel" aria-labelledby="{{window_id}}-sites-tab"> - {% if item.archaeological_sites.count %} {% trans "Archaeological sites" as archaeologicalsites_label %} + {% if item.archaeological_sites.count %} {% dynamic_table_document archaeologicalsites_label 'sites' 'operations' item.pk '' output %} + {% elif item.archaeological_sites_list %} + <h3>{{archaeologicalsites_label}}</h3> + <table class='table table-striped datatables' + id="{{window_id}}-sites-table"> + {% for values in item.archaeological_sites_list %}{% if not forloop.counter0 %} + <thead> + {% for value in values %}<th class="text-center">{{value}}</th>{% endfor %} + </thead> + {% else %} + <tr> + {% for value in values %}<td>{{value}}</td>{% endfor %} + </tr> + {% endif %} + {% endfor %} + </table> {% endif %} {% if item.parcels.count %} @@ -322,7 +348,24 @@ <div class="tab-pane fade" id="{{window_id}}-documents" role="tabpanel" aria-labelledby="{{window_id}}-documents-tab"> {% trans "Document from this operation" as operation_docs %} + {% if item.documents.count %} {% dynamic_table_document operation_docs 'documents' 'operations' item.pk '' output %} + {% elif item.documents_list %} + <h3>{{operation_docs}}</h3> + <table class='table table-striped datatables' + id="{{window_id}}-docs-table"> + {% for values in item.documents_list %}{% if not forloop.counter0 %} + <thead> + {% for value in values %}<th class="text-center">{{value}}</th>{% endfor %} + </thead> + {% else %} + <tr> + {% for value in values %}<td>{{value}}</td>{% endfor %} + </tr> + {% endif %} + {% endfor %} + </table> + {% endif %} </div> {% endif %} @@ -414,6 +457,7 @@ {% include "ishtar/blocks/sheet_json.html" %} </div> {% endif %} + {% if not is_external %} <div class="tab-pane fade" id="{{window_id}}-statistics" role="tabpanel" aria-labelledby="{{window_id}}-statistics-tab"> <h3>{% trans "Statistics" %}</h3> @@ -538,8 +582,27 @@ </div> {% endif %} </div> + {% endif %} </div> -{% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} + +<script type="text/javascript"> +$(document).ready( function () { + datatable_options = { + "dom": 'ltip', + }; + $.extend(datatable_options, datatables_static_default); + if (datatables_i18n) datatable_options['language'] = datatables_i18n; + $('.datatables').each( + function(){ + var dt_id = "#" + $(this).attr('id'); + if (! $.fn.DataTable.isDataTable(dt_id) ) { + $(dt_id).DataTable(datatable_options); + } + }); +} ); +</script> + +{% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endwith %} {% endblock %}
\ No newline at end of file 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") |