summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2024-12-01 09:53:00 +0100
committerÉtienne Loks <etienne.loks@iggdrasil.net>2025-02-19 14:45:57 +0100
commit847ae9bab5afbc2d62fa6e4b1e751096e74e4c4d (patch)
treead5c77410653a240c1224f3107bb59ffb677ca41
parenteae583cfaddfad42ccffb7e7f09773d00c051d10 (diff)
downloadIshtar-847ae9bab5afbc2d62fa6e4b1e751096e74e4c4d.tar.bz2
Ishtar-847ae9bab5afbc2d62fa6e4b1e751096e74e4c4d.zip
✨ exhibitions: "GAM" export
-rw-r--r--archaeological_finds/models_treatments.py37
-rw-r--r--archaeological_finds/templates/ishtar/forms/qa_gam_result.html9
-rw-r--r--archaeological_finds/templates/ishtar/sheet_exhibition.html15
-rw-r--r--archaeological_finds/urls.py22
-rw-r--r--archaeological_finds/views.py56
-rw-r--r--example_project/settings.py1
-rw-r--r--ishtar_common/models.py1
-rw-r--r--ishtar_common/templates/ishtar/forms/qa_base.html7
-rw-r--r--ishtar_common/templates/ishtar/forms/qa_form.html2
-rw-r--r--ishtar_common/utils.py4
-rw-r--r--ishtar_common/views.py16
11 files changed, 138 insertions, 32 deletions
diff --git a/archaeological_finds/models_treatments.py b/archaeological_finds/models_treatments.py
index d687199db..ce9066fc7 100644
--- a/archaeological_finds/models_treatments.py
+++ b/archaeological_finds/models_treatments.py
@@ -18,6 +18,12 @@
# See the file COPYING for details.
import datetime
+import lxml.etree
+import lxml.builder
+import os
+import shutil
+import tempfile
+import zipfile
from django.conf import settings
from django.contrib.gis.db import models
@@ -1379,6 +1385,36 @@ class TreatmentFile(
def natural_key(self):
return (self.year, self.index)
+ def is_gam_exportable(self):
+ self.gam_errors = []
+ is_ok = True
+ return is_ok
+
+ def gam_export(self):
+ filename = f"export_gam-{self.exhibition_start_date.year}-{slugify(self.name).replace('-', '_')}.zip"
+ maker = lxml.builder.ElementMaker()
+ document = maker.proposition(
+ maker.action("CREATION"),
+ maker.categorie("PRET"),
+ )
+ content = b'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n'
+ content += lxml.etree.tostring(document, pretty_print=True)
+ gam_dir = os.path.join(settings.MEDIA_ROOT, "GAM")
+ if not os.path.exists(gam_dir):
+ os.mkdir(gam_dir)
+ media_dir = os.path.join("GAM", str(datetime.date.today().year))
+ full_media_dir = os.path.join(settings.MEDIA_ROOT, media_dir)
+ if not os.path.exists(full_media_dir):
+ os.mkdir(full_media_dir)
+ final_name = os.path.join(full_media_dir, filename)
+ with tempfile.TemporaryDirectory() as tmp_dir_name:
+ new_file = os.path.join(tmp_dir_name, filename)
+ with zipfile.ZipFile(
+ new_file, mode="a", compression=zipfile.ZIP_DEFLATED) as zf:
+ zf.writestr("pret.xml", content)
+ shutil.move(new_file, final_name)
+ return f"{settings.MEDIA_URL}{media_dir}/{filename}"
+
@property
def short_class_name(self):
return _("Treatment request")
@@ -1598,7 +1634,6 @@ class Exhibition(
request, "archaeological_finds.add_treatmentfile"
)
if can_add_tf:
-
actions += [
(
reverse("exhibition-qa-add-loan", args=[self.pk]),
diff --git a/archaeological_finds/templates/ishtar/forms/qa_gam_result.html b/archaeological_finds/templates/ishtar/forms/qa_gam_result.html
new file mode 100644
index 000000000..15ad6e356
--- /dev/null
+++ b/archaeological_finds/templates/ishtar/forms/qa_gam_result.html
@@ -0,0 +1,9 @@
+{% extends "ishtar/forms/qa_form.html" %}
+{% load i18n inline_formset table_form %}
+{% block main_form %}
+<div class="text-center">
+ <a class="btn btn-success" href="{{gam_file}}" title="Fichier GAM" target="_blank">
+ <i class="fa fa-share-square-o"></i>&nbsp;&nbsp;Fichier GAM
+ </a>
+</div>
+{% endblock %}
diff --git a/archaeological_finds/templates/ishtar/sheet_exhibition.html b/archaeological_finds/templates/ishtar/sheet_exhibition.html
index cd37bac4b..71cad264a 100644
--- a/archaeological_finds/templates/ishtar/sheet_exhibition.html
+++ b/archaeological_finds/templates/ishtar/sheet_exhibition.html
@@ -98,13 +98,20 @@
{% for loan in item.treatment_files.all %}
<div class="tab-pane fade" id="{{window_id}}-loan{{forloop.counter}}"
role="tabpanel" aria-labelledby="{{window_id}}-loan{{forloop.counter}}-tab">
+ {% if ISHTAR_MUSEUM_GAM %}
+ <div class="row">
+ <p class="col-12 text-center m-3">
+ <a class="btn-qa btn btn-success" href="#" data-target="{% url 'exhibition-qa-gam-export' loan.pk %}" title="Export GAM">
+ <i class="fa fa-share-square-o"></i>&nbsp;&nbsp;export GAM
+ </a>
+ </p>
+ </div>
+ {% endif %}
<div class="row">
- {% with loan.exhibition_start_date|date:"SHORT_DATE_FORMAT" as exhibition_start_date %}
<dl class="col-12 col-md-6 col-lg-3 flex-wrap">
<dt>{% trans "Dates" %}</dt>
<dd>{{loan.exhibition_start_date|date:"SHORT_DATE_FORMAT"}} / {{loan.exhibition_end_date|date:"SHORT_DATE_FORMAT"}}</dd>
</dl>
- {% endwith %}
{% field_flex_detail _("Beneficiary of the loan") loan.applicant_organisation %}
{% field_flex_detail _("Scientific manager of the exhibition") loan.in_charge %}
{% field_flex_detail _("Exhibition location") loan.exhibition_location %}
@@ -116,8 +123,8 @@
{% if perm_change_basket %}
<p class="col-12 col-md-6 col-lg-3 flex-wrap">
- <a class="wait-button btn btn-success" href="/find_basket_modification_add/{{loan.associated_basket.pk}}/?back_url={% url 'display-exhibition' loan.pk %}" title="{% trans 'Manage basket' %}">
- <i class="fa fa-shopping-basket"></i>&nbsp; {% trans "manage items of this basket" %}
+ <a class="wait-button btn btn-success" href="/find_basket_modification_add/{{loan.associated_basket.pk}}/?back_url={% url 'display-exhibition' loan.pk %}" title="{% trans 'Manage basket' %}">
+ <i class="fa fa-shopping-basket"></i>&nbsp; {% trans "manage items of this basket" %}
</a>
</p>
{% endif %}
diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py
index 8ddea481d..b99140e00 100644
--- a/archaeological_finds/urls.py
+++ b/archaeological_finds/urls.py
@@ -451,13 +451,6 @@ urlpatterns = [
name="exhibition-create",
),
path(
- "exhibition/qa/add-loan/<int:pks>/",
- check_permissions(
- ["archaeological_finds.add_treatmentfile"]
- )(views.QAExhibitionLoanFormView.as_view()),
- name="exhibition-qa-add-loan",
- ),
- path(
"exhibition/modify/<int:pk>/",
check_permissions(
["archaeological_finds.change_exhibition",
@@ -481,6 +474,21 @@ urlpatterns = [
name="exhibition-revert"
),
path(
+ "exhibition/qa/add-loan/<int:pks>/",
+ check_permissions(
+ ["archaeological_finds.add_treatmentfile"]
+ )(views.QAExhibitionLoanFormView.as_view()),
+ name="exhibition-qa-add-loan",
+ ),
+ path(
+ "exhibition/qa/gam-export/<int:pk>/",
+ check_permissions(
+ ["archaeological_finds.view_treatmentfile",
+ "archaeological_finds.view_own_treatmentfile"]
+ )(views.qa_gam_export_views),
+ name="exhibition-qa-gam-export",
+ ),
+ path(
"exhibition/<step>/",
check_permissions(
["archaeological_finds.view_exhibition",
diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py
index 11509f4aa..7fdc83197 100644
--- a/archaeological_finds/views.py
+++ b/archaeological_finds/views.py
@@ -24,14 +24,14 @@ from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.http import HttpResponseRedirect, HttpResponse, Http404
-from django.shortcuts import redirect
+from django.shortcuts import redirect, render
from django.urls import reverse
from ishtar_common.utils import ugettext_lazy as _, BSMessage
from django.views.generic import TemplateView
from django.views.generic.edit import CreateView, FormView, UpdateView
-from ishtar_common.models import get_current_profile, IshtarUser, QuickAction
+from ishtar_common.models import get_current_profile, IshtarUser
from archaeological_operations.models import AdministrativeAct, Operation
from archaeological_context_records.models import ContextRecord
from archaeological_finds import models
@@ -143,7 +143,13 @@ get_treatmentfile = get_item(
search_form=forms.TreatmentFileSelect,
)
-show_exhibition = show_item(models.Exhibition, "exhibition")
+
+def show_exhibition_extra(request, exhibition):
+ return {"ISHTAR_MUSEUM_GAM": settings.ISHTAR_MUSEUM_GAM}
+
+
+show_exhibition = show_item(models.Exhibition, "exhibition",
+ extra_dct=show_exhibition_extra)
revert_exhibition = revert_item(models.Exhibition)
get_exhibition = get_item(
models.Exhibition, "get_exhibition", "exhibition",
@@ -1410,16 +1416,7 @@ class QAExhibitionLoanFormView(QAItemForm):
base_url = "exhibition-qa-add-loan"
icon = "fa fa-plus"
action_name = _("Create")
-
- def get_quick_action(self):
- return QuickAction(
- url=self.base_url,
- icon_class=self.icon,
- text=self.page_name,
- rights=[
- "archaeological_finds.add_treatmentfile",
- ],
- )
+ permisssions = ["archaeological_finds.add_treatmentfile"]
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
@@ -1431,7 +1428,7 @@ class QAExhibitionLoanFormView(QAItemForm):
if not self.items[0].associated_basket_id:
data["messages"] = [
BSMessage(
- _("No basket associted to the exhibition."),
+ _("No basket associated to the exhibition."),
"danger", "fa fa-exclamation-triangle")
]
return data
@@ -1439,3 +1436,34 @@ class QAExhibitionLoanFormView(QAItemForm):
def form_valid(self, form):
form.save()
return HttpResponseRedirect(reverse("success"))
+
+
+def qa_gam_export_views(request, pk, *args, **kwargs):
+ # TODO: verify treatement file own view
+ try:
+ loan = models.TreatmentFile.objects.get(pk=pk)
+ except models.TreatmentFile.DoesNotExist:
+ raise Http404()
+ dct = {
+ "loan": loan,
+ "page_name": "Export GAM",
+ "url": reverse("exhibition-qa-gam-export", args=[pk]),
+ "icon": "fa fa-share-square-o",
+ "action_name": "Export GAM",
+ "cancel_name": _("Close")
+ }
+ if request.POST:
+ dct["gam_file"] = loan.gam_export()
+ dct["unavailable"] = True
+ return render(request, "ishtar/forms/qa_gam_result.html", dct)
+ if loan.is_gam_exportable():
+ dct["messages"] = [
+ BSMessage("Prêt OK pour export GAM", "info", no_dismiss=True)
+ ]
+ else:
+ dct["messages"] = [
+ BSMessage(error, "danger", no_dismiss=True)
+ for error in loan.gam_errors
+ ]
+ dct["unavailable"] = True
+ return render(request, "ishtar/forms/qa_form.html", dct)
diff --git a/example_project/settings.py b/example_project/settings.py
index 266551171..a4a2b72f5 100644
--- a/example_project/settings.py
+++ b/example_project/settings.py
@@ -275,6 +275,7 @@ ISHTAR_MAP_MAX_ITEMS = 50000
ISHTAR_QRCODE_VERSION = 6 # density of the QR code
ISHTAR_QRCODE_SCALE = 2 # scale of the QR code
ISHTAR_DEFAULT_YEAR = 1900
+ISHTAR_MUSEUM_GAM = False # France - AlimGAM export of exhibitions
ISHTAR_SLUGS = {
"document-publisher": ["publisher"],
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index 0690693f5..cebfccecb 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -2320,6 +2320,7 @@ class DocumentTemplate(models.Model):
prefix += k
return list(set(new_filter))
+ # snake_case ; snake_case[number] ; snake_case[number]:filter ; snake_case:filter
ITEM_RE = r"([A-Za-z0-9_.]*)(?:[\[\]0-9-:])*(?:\|[^}]+)*"
BASE_RE = [
# {{ key1.key2 }}
diff --git a/ishtar_common/templates/ishtar/forms/qa_base.html b/ishtar_common/templates/ishtar/forms/qa_base.html
index f6e43ad15..822f39c58 100644
--- a/ishtar_common/templates/ishtar/forms/qa_base.html
+++ b/ishtar_common/templates/ishtar/forms/qa_base.html
@@ -3,7 +3,7 @@
<div class="modal-dialog {% if modal_size == 'large' %}modal-lg {% elif modal_size == 'small'%}modal-sm {% endif%}modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
- <h2>{{page_name|safe}}</h2>
+ <h2>{% if icon %}<i class="{{icon}}"></i>&nbsp;&nbsp;{% endif %}{{page_name|safe}}</h2>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
@@ -48,6 +48,7 @@
</div>
<div class="modal-footer">
{% block footer %}
+ {% if not unavailable %}
<button type="submit" id="submit_form" name='validate'
value="validate" class="btn btn-success">
{% if action_name %}
@@ -56,9 +57,11 @@
{% trans "Modify" %}
{% endif %}
</button>
+ {% endif %}
<button type="button" data-dismiss="modal"
aria-label="Close" class="btn btn-secondary">
- {% trans "Cancel" %}
+ {% if cancel_name %}{{cancel_name}}{% else %}
+ {% trans "Cancel" %}{% endif %}
</button>
{% endblock %}
</div>
diff --git a/ishtar_common/templates/ishtar/forms/qa_form.html b/ishtar_common/templates/ishtar/forms/qa_form.html
index 178910215..ef4f9f149 100644
--- a/ishtar_common/templates/ishtar/forms/qa_form.html
+++ b/ishtar_common/templates/ishtar/forms/qa_form.html
@@ -6,9 +6,11 @@
<div class="alert alert-{{message.type}} alert-dismissible fade show" role="alert">
{% if message.icon %}<i class="{{message.icon}}" aria-hidden="true"></i>&nbsp;&nbsp;{% endif %}
{{message.message}}
+ {% if not message.no_dismiss %}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
+ {% endif %}
</div>
{% endfor %}
{% for error in form.non_field_errors %}
diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py
index 1f0b099e7..af4ea06dc 100644
--- a/ishtar_common/utils.py
+++ b/ishtar_common/utils.py
@@ -1387,10 +1387,12 @@ def get_random_item_image_link(request):
class BSMessage:
- def __init__(self, message, message_type="info", icon=None):
+ def __init__(self, message, message_type="info", icon=None,
+ no_dismiss=False):
self.message = message
self.type = message_type
self.icon = icon
+ self.no_dismiss = no_dismiss
def convert_coordinates_to_point(x, y, z=None, srid=4326):
diff --git a/ishtar_common/views.py b/ishtar_common/views.py
index ba9ccb316..32f23f91d 100644
--- a/ishtar_common/views.py
+++ b/ishtar_common/views.py
@@ -73,6 +73,7 @@ from ishtar_common import wizards
from ishtar_common.data_importer import ImporterError
from ishtar_common.forms import FinalForm, FinalDeleteForm, reverse_lazy
from ishtar_common.models import get_current_profile
+from ishtar_common.models_common import QuickAction
from ishtar_common.templatetags.link_to_window import simple_link_to_window
from ishtar_common.utils_migrations import HOMEPAGE_TITLE
from ishtar_common.utils import (
@@ -3139,10 +3140,19 @@ class QAItemForm(IshtarMixin, LoginRequiredMixin, FormView):
modal_size = None # large, small or None (medium)
icon = "fa fa-pencil"
action_name = None
+ permissions = []
def get_quick_action(self):
- # if not listed in QUICK_ACTIONS overload this method
- return self.model.get_quick_action_by_url(self.base_url)
+ quick_action = self.model.get_quick_action_by_url(self.base_url)
+ if quick_action:
+ return quick_action
+ # if not listed in QUICK_ACTIONS
+ return QuickAction(
+ url=self.base_url,
+ icon_class=self.icon,
+ text=self.page_name,
+ rights=self.permissions
+ )
def pre_dispatch(self, request, *args, **kwargs):
if not self.model:
@@ -3192,7 +3202,7 @@ class QAItemForm(IshtarMixin, LoginRequiredMixin, FormView):
)
def get_context_data(self, **kwargs):
- data = super(QAItemForm, self).get_context_data(**kwargs)
+ data = super().get_context_data(**kwargs)
data["url"] = self.url
data["items"] = self.items
data["modal_size"] = self.modal_size