summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.md6
-rw-r--r--archaeological_context_records/forms.py105
-rw-r--r--archaeological_context_records/models.py19
-rw-r--r--archaeological_context_records/templates/ishtar/forms/qa_contextrecord_duplicate.html39
-rw-r--r--archaeological_context_records/templates/ishtar/sheet_contextrecord.html16
-rw-r--r--archaeological_context_records/urls.py4
-rw-r--r--archaeological_context_records/views.py23
-rw-r--r--archaeological_finds/forms.py2
-rw-r--r--archaeological_operations/forms.py88
-rw-r--r--archaeological_operations/models.py38
-rw-r--r--archaeological_operations/templates/ishtar/forms/qa_operation_duplicate.html39
-rw-r--r--archaeological_operations/templates/ishtar/forms/qa_site_duplicate.html23
-rw-r--r--archaeological_operations/urls.py9
-rw-r--r--archaeological_operations/views.py287
-rw-r--r--ishtar_common/forms_common.py68
-rw-r--r--ishtar_common/models.py79
-rw-r--r--ishtar_common/templates/ishtar/forms/qa_document_duplicate.html95
-rw-r--r--ishtar_common/templates/ishtar/sheet_document.html2
-rw-r--r--ishtar_common/urls.py4
-rw-r--r--ishtar_common/views.py26
-rw-r--r--scss/custom.scss4
21 files changed, 784 insertions, 192 deletions
diff --git a/CHANGES.md b/CHANGES.md
index 4189a0c15..82aaa3d2a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,11 @@
Ishtar changelog
================
+v3.0.4 - 2020-XX-XX
+-------------------
+### Features ###
+- Sheet actions: add duplicate for site, operation, document and context record
+
v3.0.3 - 2020-02-24
-------------------
### Bug fixes ###
@@ -21,5 +26,4 @@ v3.0.1 - 2020-02-11
v3.0.0
------
-
A lot of changes since v2. Main change is python3 and Django 1.11 support.
diff --git a/archaeological_context_records/forms.py b/archaeological_context_records/forms.py
index 2061ecc1f..87cad2c66 100644
--- a/archaeological_context_records/forms.py
+++ b/archaeological_context_records/forms.py
@@ -147,6 +147,27 @@ class RecordFormMultiSelection(LockForm, MultiSearchForm):
validators=[valid_ids(models.ContextRecord)])
+def get_init_parcel(form, operation, prefix=""):
+ parcels = operation.parcels.all()
+ sort = lambda x: (x.town.name, x.section)
+ parcels = sorted(parcels, key=sort)
+ for key, gparcels in groupby(parcels, sort):
+ form.fields[prefix + 'parcel'].choices.append(
+ (" - ".join([k for k in key if k]),
+ [(parcel.pk, parcel.short_label) for parcel in gparcels])
+ )
+ if len(form.fields[prefix + 'parcel'].choices) == 1 and \
+ (prefix + 'town') in form.fields:
+ # only the empty choice is available
+ form.fields.pop(prefix + 'parcel')
+ form.fields[prefix + 'town'].required = True
+ if (prefix + 'town') in form.fields:
+ if form.fields[prefix + 'town'].required:
+ form.fields[prefix + 'town'].choices = [] # remove the empty choice
+ form.fields[prefix + 'town'].choices += [
+ (t.pk, str(t)) for t in operation.towns.all()]
+
+
class RecordFormGeneral(CustomForm, ManageOldType):
HEADERS = {}
form_label = _("General")
@@ -279,25 +300,7 @@ class RecordFormGeneral(CustomForm, ManageOldType):
if operation:
self.fields['operation_id'].initial = operation.pk
- parcels = operation.parcels.all()
- sort = lambda x: (x.town.name, x.section)
- parcels = sorted(parcels, key=sort)
- for key, gparcels in groupby(parcels, sort):
- self.fields['parcel'].choices.append(
- (" - ".join([k for k in key if k]),
- [(parcel.pk, parcel.short_label) for parcel in gparcels])
- )
- if len(self.fields['parcel'].choices) == 1 and \
- 'town' in self.fields:
- # only the empty choice is available
- self.fields.pop('parcel')
- self.fields['town'].required = True
- if 'town' in self.fields:
- if self.fields['town'].required:
- self.fields['town'].choices = [] # remove the empty choice
- self.fields['town'].choices += [(t.pk, str(t))
- for t in operation.towns.all()]
-
+ get_init_parcel(self, operation)
self.fields['archaeological_site'].choices += [
(site.pk, str(site))
for site in operation.archaeological_sites.all()
@@ -478,3 +481,67 @@ class QAOperationCR(IshtarForm):
else:
data["town_id"] = self.cleaned_data['town']
models.ContextRecord.objects.create(**data)
+
+
+class QAContextRecordDuplicateForm(IshtarForm):
+ qa_label = forms.CharField(label=_("ID"), max_length=None, required=True)
+ qa_parcel = forms.ChoiceField(label=_(u"Parcel"), choices=[])
+ qa_town = forms.ChoiceField(label=_(u"Town"), choices=[], required=False)
+ qa_unit = forms.ChoiceField(label=_(u"Context record type"), required=False,
+ choices=[])
+
+ TYPES = [
+ FieldType('qa_unit', models.Unit),
+ ]
+
+ def __init__(self, *args, **kwargs):
+ self.user = None
+ if 'user' in kwargs:
+ self.user = kwargs.pop('user')
+ if hasattr(self.user, 'ishtaruser'):
+ self.user = self.user.ishtaruser
+ self.context_record = kwargs.pop('items')[0]
+ super(QAContextRecordDuplicateForm, self).__init__(*args, **kwargs)
+
+ profile = IshtarSiteProfile.get_current_profile()
+ self.fields['qa_parcel'].choices = [('', '--')]
+ if not profile.parcel_mandatory:
+ self.fields['qa_parcel'].required = False
+ self.fields['qa_town'].choices = [('', '--')]
+ else:
+ self.fields.pop('qa_town')
+
+ get_init_parcel(self, self.context_record.operation, prefix="qa_")
+
+ if "qa_town" in self.fields and self.context_record.town:
+ self.fields["qa_town"].initial = self.context_record.town.pk
+ if "qa_parcel" in self.fields and self.context_record.parcel:
+ self.fields["qa_parcel"].initial = self.context_record.parcel.pk
+
+ self.fields['qa_label'].initial = (
+ self.context_record.label or "") + str(_(" - duplicate"))
+ if self.context_record.unit:
+ self.fields["qa_unit"].initial = self.context_record.unit.pk
+
+ def save(self):
+ data = {"label": self.cleaned_data["qa_label"]}
+ if self.cleaned_data.get("qa_unit", None):
+ try:
+ data["unit"] = models.Unit.objects.get(pk=int(
+ self.cleaned_data["qa_unit"]), available=True)
+ except models.Unit.DoesNotExist:
+ pass
+ if self.cleaned_data.get("qa_town", None):
+ try:
+ data["town"] = Town.objects.get(pk=int(
+ self.cleaned_data["qa_town"]))
+ except Town.DoesNotExist:
+ pass
+ if self.cleaned_data.get("qa_parcel", None):
+ try:
+ data["parcel"] = Parcel.objects.get(pk=int(
+ self.cleaned_data["qa_parcel"]))
+ except Parcel.DoesNotExist:
+ pass
+ return self.context_record.duplicate(
+ self.user, data=data)
diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py
index febc7cfdd..9b1483e0d 100644
--- a/archaeological_context_records/models.py
+++ b/archaeological_context_records/models.py
@@ -459,7 +459,11 @@ class ContextRecord(BulkUpdatedItem, DocumentItem, BaseHistorizedItem,
rights=['change_contextrecord', 'change_own_contextrecord']
)
QUICK_ACTIONS = [
- QA_LOCK
+ QA_LOCK,
+ QuickAction(
+ url="contextrecord-qa-duplicate", icon_class="fa fa-clone",
+ text=_(u"Duplicate"), target="one",
+ rights=['change_contextrecord', 'change_own_contextrecord']),
]
history = HistoricalRecords(bases=[HistoryModel])
@@ -723,6 +727,19 @@ class ContextRecord(BulkUpdatedItem, DocumentItem, BaseHistorizedItem,
def show_url(self):
return reverse('show-contextrecord', args=[self.pk, ''])
+ def get_extra_actions(self, request):
+ # url, base_text, icon, extra_text, extra css class, is a quick action
+ actions = super(ContextRecord, self).get_extra_actions(request)
+
+ #is_locked = hasattr(self, "is_locked") and self.is_locked(request.user)
+ can_edit_cr = self.can_do(request, 'change_contextrecord')
+ if can_edit_cr:
+ actions += [
+ (reverse("contextrecord-qa-duplicate", args=[self.pk]),
+ _("Duplicate"), "fa fa-clone", "", "", True),
+ ]
+ return actions
+
@classmethod
def get_query_owns(cls, ishtaruser):
q = cls._construct_query_own(
diff --git a/archaeological_context_records/templates/ishtar/forms/qa_contextrecord_duplicate.html b/archaeological_context_records/templates/ishtar/forms/qa_contextrecord_duplicate.html
new file mode 100644
index 000000000..a018a67fa
--- /dev/null
+++ b/archaeological_context_records/templates/ishtar/forms/qa_contextrecord_duplicate.html
@@ -0,0 +1,39 @@
+{% extends "ishtar/forms/qa_base.html" %}
+{% load i18n inline_formset table_form %}
+
+{% block main_form %}
+{% if form.non_field_errors %}
+<div class="alert alert-danger" role="alert">
+ {{form.non_field_errors}}
+</div>
+{% endif %}
+<div class="form-row">
+ <div class="form-group col-lg-12 required full-width">
+ <label>{% trans "Operation" %}{% trans ":" %}</label>
+ {{operation}}
+ </div>
+</div>
+{% with force_large_col=True %}
+<div class="form-row">
+ {% with form.qa_label as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+<div class="form-row">
+ {% with form.qa_parcel as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% if form.qa_town %}<div class="form-row">
+ {% with form.qa_town as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>{% endif %}
+{% if form.qa_parcel %}<div class="form-row">
+ {% with form.qa_unit as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>{% endif %}
+{% endwith %}
+{% endblock %}
+
diff --git a/archaeological_context_records/templates/ishtar/sheet_contextrecord.html b/archaeological_context_records/templates/ishtar/sheet_contextrecord.html
index a7d01c6dd..ffeba676c 100644
--- a/archaeological_context_records/templates/ishtar/sheet_contextrecord.html
+++ b/archaeological_context_records/templates/ishtar/sheet_contextrecord.html
@@ -104,7 +104,6 @@
<div class="tab-pane fade show active" id="{{window_id}}-identification"
role="tabpanel" aria-labelledby="{{window_id}}-identification-tab">
- {% if has_image %}
<div class="clearfix">
<div class="card float-left col-12 col-md-6 col-lg-4">
{% include "ishtar/blocks/window_image.html" %}
@@ -116,17 +115,10 @@
</p>
</div>
</div>
- </div>
- {% else %}
- <div>
- <p class='window-refs'>{{ item.parcel.short_label }}</p>
- <p class="window-refs">{{ item.label|default:"" }}</p>
- {% include "ishtar/blocks/sheet_external_id.html" %}
- </div>
- {% endif %}
- <div class='row'>
- {% field_flex_2 "Complete ID" item.full_label %}
- {% field_flex_2 "Type" item.unit %}
+ <div class='row'>
+ {% field_flex_2 "Complete ID" item.full_label %}
+ {% field_flex_2 "Type" item.unit %}
+ </div>
</div>
<h3>{% trans "Description"%}</h3>
<div class="row">
diff --git a/archaeological_context_records/urls.py b/archaeological_context_records/urls.py
index d5c2c7d7d..434aa79f9 100644
--- a/archaeological_context_records/urls.py
+++ b/archaeological_context_records/urls.py
@@ -95,5 +95,9 @@ urlpatterns = [
url(r'^contextrecord-qa-lock/(?P<pks>[0-9-]+)?/$',
views.QAContextRecordLockView.as_view(), name='contextrecord-qa-lock',
kwargs={"model": models.ContextRecord}),
+ url(r'^contextrecord-qa-duplicate/(?P<pks>[0-9-]+)?/$',
+ check_rights(['change_contextrecord', 'change_own_contextrecord'])(
+ views.QAContextRecordDuplicateFormView.as_view()),
+ name='contextrecord-qa-duplicate'),
]
diff --git a/archaeological_context_records/views.py b/archaeological_context_records/views.py
index 74def46a4..215dd2b4a 100644
--- a/archaeological_context_records/views.py
+++ b/archaeological_context_records/views.py
@@ -233,3 +233,26 @@ class QAContextRecordLockView(QABaseLockView):
model = models.ContextRecord
base_url = "contextrecord-qa-lock"
+
+class QAContextRecordDuplicateFormView(QAItemForm):
+ template_name = 'ishtar/forms/qa_contextrecord_duplicate.html'
+ model = models.ContextRecord
+ page_name = _("Duplicate")
+ form_class = forms.QAContextRecordDuplicateForm
+ base_url = "contextrecord-qa-duplicate"
+
+ def get_form_kwargs(self):
+ kwargs = super(QAContextRecordDuplicateFormView, self).get_form_kwargs()
+ kwargs['user'] = self.request.user
+ return kwargs
+
+ def form_valid(self, form):
+ form.save()
+ return HttpResponseRedirect(reverse("success"))
+
+ def get_context_data(self, **kwargs):
+ data = super(QAContextRecordDuplicateFormView, self).get_context_data(
+ **kwargs)
+ data['action_name'] = _(u"Duplicate")
+ data["operation"] = self.items[0].operation
+ return data
diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py
index babc56876..bf5854cc6 100644
--- a/archaeological_finds/forms.py
+++ b/archaeological_finds/forms.py
@@ -770,7 +770,7 @@ class QAFindDuplicateForm(IshtarForm):
self.find = kwargs.pop('items')[0]
super(QAFindDuplicateForm, self).__init__(*args, **kwargs)
self.fields['label'].initial = self.find.label + str(
- _(u" - duplicate"))
+ _(" - duplicate"))
self.fields['denomination'].initial = self.find.denomination or ""
def save(self):
diff --git a/archaeological_operations/forms.py b/archaeological_operations/forms.py
index 18534fe9d..eab5de15b 100644
--- a/archaeological_operations/forms.py
+++ b/archaeological_operations/forms.py
@@ -1876,3 +1876,91 @@ class QAOperationFormMulti(QAForm):
return ""
return value
+
+class QAOperationDuplicateForm(IshtarForm):
+ qa_code_patriarche = forms.CharField(
+ max_length=500, widget=OAWidget, label=_("Code PATRIARCHE"),
+ required=False)
+ qa_year = forms.IntegerField(label=_("Year"), required=False,
+ validators=[validators.MinValueValidator(1000),
+ validators.MaxValueValidator(2100)])
+ qa_common_name = forms.CharField(label=_("Generic name"), required=False,
+ max_length=500, widget=forms.Textarea)
+ qa_operation_type = forms.ChoiceField(label=_("Operation type"), choices=[])
+
+ TYPES = [
+ FieldType('qa_operation_type', models.OperationType),
+ ]
+
+ def __init__(self, *args, **kwargs):
+ self.user = None
+ if 'user' in kwargs:
+ self.user = kwargs.pop('user')
+ if hasattr(self.user, 'ishtaruser'):
+ self.user = self.user.ishtaruser
+ self.operation = kwargs.pop('items')[0]
+ super(QAOperationDuplicateForm, self).__init__(*args, **kwargs)
+
+ self.fields['qa_year'].initial = self.operation.year
+ self.fields['qa_common_name'].initial = self.operation.common_name
+
+ self.fields["qa_operation_type"].initial = \
+ self.operation.operation_type.pk
+
+ def clean_qa_code_patriarche(self):
+ code = self.cleaned_data['qa_code_patriarche']
+ if models.Operation.objects \
+ .filter(code_patriarche=code).count():
+ raise forms.ValidationError(_("This code already exists."))
+ return code
+
+ def save(self):
+ data = {"operation_code": None}
+ for k in ("code_patriarche", "common_name", "year"):
+ data[k] = self.cleaned_data.get("qa_" + k, None)
+ try:
+ data["operation_type"] = models.OperationType.objects.get(
+ pk=self.cleaned_data["qa_operation_type"], available=True
+ )
+ except models.OperationType.DoesNotExist:
+ return
+ operation = self.operation.duplicate(self.user, data=data)
+ # clear associated sites
+ operation.archaeological_sites.clear()
+ operation.skip_history_when_saving = True
+ operation._cached_label_checked = False
+ operation._search_updated = False
+ operation._no_move = True
+ operation.save() # regen of labels
+ return operation
+
+
+class QAArchaeologicalSiteDuplicateForm(IshtarForm):
+ qa_reference = forms.CharField(label=_("Reference"), max_length=200)
+ qa_name = forms.CharField(label=_("Name"), max_length=200, required=False)
+
+ def __init__(self, *args, **kwargs):
+ self.user = None
+ if 'user' in kwargs:
+ self.user = kwargs.pop('user')
+ if hasattr(self.user, 'ishtaruser'):
+ self.user = self.user.ishtaruser
+ self.site = kwargs.pop('items')[0]
+ super(QAArchaeologicalSiteDuplicateForm, self).__init__(*args, **kwargs)
+
+ self.fields['qa_reference'].initial = (
+ self.site.reference or "") + str(_(" - duplicate"))
+ self.fields['qa_name'].initial = self.site.name
+
+ def clean_qa_reference(self):
+ reference = self.cleaned_data['qa_reference']
+ if models.ArchaeologicalSite.objects \
+ .filter(reference=reference).count():
+ raise forms.ValidationError(_("This reference already exists."))
+ return reference
+
+ def save(self):
+ data = {}
+ for k in ("name", "reference"):
+ data[k] = self.cleaned_data.get("qa_" + k, None)
+ return self.site.duplicate(self.user, data=data)
diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py
index 402f45485..54a44999c 100644
--- a/archaeological_operations/models.py
+++ b/archaeological_operations/models.py
@@ -283,7 +283,12 @@ class ArchaeologicalSite(DocumentItem, BaseHistorizedItem, QRCodeItem,
rights=['change_operation', 'change_own_operation']
)
QUICK_ACTIONS = [
- QA_LOCK
+ QA_LOCK,
+ QuickAction(
+ url="site-qa-duplicate", icon_class="fa fa-clone",
+ text=_("Duplicate"), target="one",
+ rights=['change_archaeologicalsite',
+ 'change_own_archaeologicalsite']),
]
objects = SiteManager()
@@ -409,6 +414,22 @@ class ArchaeologicalSite(DocumentItem, BaseHistorizedItem, QRCodeItem,
return Find.objects.filter(
base_finds__context_record__archaeological_site__pk=self.pk)
+ def get_extra_actions(self, request):
+ """
+ For sheet template
+ """
+ # url, base_text, icon, extra_text, extra css class, is a quick action
+ actions = super(ArchaeologicalSite, self).get_extra_actions(request)
+ # is_locked = self.is_locked(request.user)
+
+ can_edit_site = self.can_do(request, 'change_archaeologicalsite')
+ if can_edit_site:
+ actions += [
+ (reverse("site-qa-duplicate", args=[self.pk]),
+ _("Duplicate"), "fa fa-clone", "", "", True),
+ ]
+ return actions
+
@classmethod
def _get_query_owns_dicts(cls, ishtaruser, no_rel=False):
profile = ishtaruser.current_profile
@@ -932,7 +953,11 @@ class Operation(ClosedItem, DocumentItem, BaseHistorizedItem, QRCodeItem,
rights=['change_operation', 'change_own_operation']
)
QUICK_ACTIONS = [
- QA_EDIT, QA_LOCK
+ QA_EDIT, QA_LOCK,
+ QuickAction(
+ url="operation-qa-duplicate", icon_class="fa fa-clone",
+ text=_("Duplicate"), target="one",
+ rights=['change_operation', 'change_own_operation']),
]
UP_MODEL_QUERY = {
@@ -1352,9 +1377,16 @@ class Operation(ClosedItem, DocumentItem, BaseHistorizedItem, QRCodeItem,
"""
# url, base_text, icon, extra_text, extra css class, is a quick action
actions = super(Operation, self).get_extra_actions(request)
+ is_locked = self.is_locked(request.user)
+ can_edit_operation = self.can_do(request, 'change_operation')
+ if can_edit_operation:
+ actions += [
+ (reverse("operation-qa-duplicate", args=[self.pk]),
+ _("Duplicate"), "fa fa-clone", "", "", True),
+ ]
can_add_cr = self.can_do(request, 'add_contextrecord')
- if can_add_cr and not self.is_locked(request.user):
+ if can_add_cr and not is_locked:
actions += [
(reverse('operation-qa-contextrecord', args=[self.pk]),
_("Add context record"), "fa fa-plus",
diff --git a/archaeological_operations/templates/ishtar/forms/qa_operation_duplicate.html b/archaeological_operations/templates/ishtar/forms/qa_operation_duplicate.html
new file mode 100644
index 000000000..3c07cdf65
--- /dev/null
+++ b/archaeological_operations/templates/ishtar/forms/qa_operation_duplicate.html
@@ -0,0 +1,39 @@
+{% extends "ishtar/forms/qa_base.html" %}
+{% load i18n inline_formset table_form %}
+
+{% block main_form %}
+{% if form.non_field_errors %}
+<div class="alert alert-danger" role="alert">
+ {{form.non_field_errors}}
+</div>
+{% endif %}
+<div class="form-row">
+ {% with form.qa_code_patriarche as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% with force_large_col=True %}
+<div class="form-row">
+ {% with form.qa_year as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+<div class="form-row">
+ {% with form.qa_common_name as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+<div class="form-row">
+ {% with form.qa_operation_type as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endwith %}
+{% comment %}
+<p>
+ <label for="id_modify">{% trans "Edit the duplicated find" %}{% trans ":" %}</label>
+ <input type="checkbox" name="modify" id="id_modify">
+</p>
+{% endcomment %}
+{% endblock %}
+
diff --git a/archaeological_operations/templates/ishtar/forms/qa_site_duplicate.html b/archaeological_operations/templates/ishtar/forms/qa_site_duplicate.html
new file mode 100644
index 000000000..3affec502
--- /dev/null
+++ b/archaeological_operations/templates/ishtar/forms/qa_site_duplicate.html
@@ -0,0 +1,23 @@
+{% extends "ishtar/forms/qa_base.html" %}
+{% load i18n inline_formset table_form %}
+
+{% block main_form %}
+{% if form.non_field_errors %}
+<div class="alert alert-danger" role="alert">
+ {{form.non_field_errors}}
+</div>
+{% endif %}
+{% with force_large_col=True %}
+<div class="form-row">
+ {% with form.qa_reference as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+<div class="form-row">
+ {% with form.qa_name as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endwith %}
+{% endblock %}
+
diff --git a/archaeological_operations/urls.py b/archaeological_operations/urls.py
index a52aab3de..a1e658c04 100644
--- a/archaeological_operations/urls.py
+++ b/archaeological_operations/urls.py
@@ -185,11 +185,20 @@ urlpatterns = [
check_rights(['change_operation', 'change_own_operation'])(
views.QAOperationForm.as_view()),
name='operation-qa-bulk-update-confirm', kwargs={"confirm": True}),
+ url(r'^operation-qa-duplicate/(?P<pks>[0-9-]+)?/$',
+ check_rights(['change_operation', 'change_own_operation'])(
+ views.QAOperationdDuplicateFormView.as_view()),
+ name='operation-qa-duplicate'),
url(r'^operation-qa-lock/(?P<pks>[0-9-]+)?/$',
views.QAOperationLockView.as_view(), name='operation-qa-lock',
kwargs={"model": models.Operation}),
+ url(r'^site-qa-duplicate/(?P<pks>[0-9-]+)?/$',
+ check_rights(['change_archaeologicalsite',
+ 'change_own_archaeologicalsite'])(
+ views.QAArchaeologicalSiteDuplicateFormView.as_view()),
+ name='site-qa-duplicate'),
url(r'^site-qa-lock/(?P<pks>[0-9-]+)?/$',
views.QASiteLockView.as_view(), name='site-qa-lock',
kwargs={"model": models.ArchaeologicalSite}),
diff --git a/archaeological_operations/views.py b/archaeological_operations/views.py
index 38280ccbf..657ee7b1e 100644
--- a/archaeological_operations/views.py
+++ b/archaeological_operations/views.py
@@ -28,33 +28,15 @@ from django.shortcuts import render, redirect
from ishtar_common.utils import ugettext_lazy as _, pgettext_lazy
from archaeological_operations import models
-from archaeological_operations.forms import ArchaeologicalSiteForm, \
- OperationFormSelection, OperationFormFileChoice, OperationFormGeneral, \
- OperationFormModifGeneral, FinalOperationClosingForm, \
- CollaboratorForm, ArchaeologicalSiteFormSet, OperationFormPreventive, \
- OperationFormPreventiveDiag, TownFormset, SelectedTownFormset, \
- SelectedParcelGeneralFormSet, SelectedParcelFormSet, RemainForm, \
- PeriodForm, RecordRelationsFormSet, OperationFormAbstract, \
- OperationDeletionForm, SiteFormSelection, \
- AdministrativeActOpeFormSelection, AdministrativeActOpeForm, \
- AdministrativeActOpeModifForm, FinalAdministrativeActDeleteForm, \
- AdministrativeActRegisterFormSelection, DocumentGenerationAdminActForm, \
- SiteForm, SiteTownFormset, SiteUnderwaterForm, check_underwater_module, \
- CourtOrderedSeizureForm, SiteSelect, OperationSelect, \
- QAOperationFormMulti, OperationFormMultiSelection, SiteFormMultiSelection
-from archaeological_operations.wizards import has_associated_file, \
- is_preventive, is_judiciary, OperationWizard, OperationModificationWizard, \
- OperationClosingWizard, OperationDeletionWizard, SiteSearch, \
- OperationAdministrativeActWizard, OperationEditAdministrativeActWizard, \
- AdministrativeActDeletionWizard, SiteWizard, SiteModificationWizard, \
- SiteDeletionWizard, OperationSearch
+from archaeological_operations import forms
+from archaeological_operations import wizards
from ishtar_common.forms import ClosingDateFormSelection, FinalForm, \
FinalDeleteForm
from ishtar_common.models import get_current_profile, IshtarSiteProfile, \
DocumentTemplate
from ishtar_common.utils import put_session_message, check_rights_condition
from ishtar_common.views import gen_generate_doc, QAItemEditForm, \
- QABaseLockView, wizard_is_available
+ QABaseLockView, wizard_is_available, QAItemForm
from ishtar_common.views_item import get_item, show_item, revert_item, \
new_qa_item
from ishtar_common.wizards import SearchWizard
@@ -108,7 +90,7 @@ def autocomplete_archaeologicalsite(request):
new_archaeologicalsite = new_qa_item(models.ArchaeologicalSite,
- ArchaeologicalSiteForm, many=True)
+ forms.ArchaeologicalSiteForm, many=True)
def autocomplete_operation(request):
@@ -159,7 +141,7 @@ def get_available_operation_code(request, year=None):
get_operation = get_item(models.Operation, 'get_operation', 'operation',
- search_form=OperationSelect)
+ search_form=forms.OperationSelect)
show_operation = show_item(models.Operation, 'operation')
revert_operation = revert_item(models.Operation)
@@ -183,28 +165,28 @@ def dashboard_operation(request, *args, **kwargs):
return render(request, 'ishtar/dashboards/dashboard_operation.html', dct)
-operation_search_wizard = OperationSearch.as_view(
- [('general-operation_search', OperationFormSelection)],
+operation_search_wizard = wizards.OperationSearch.as_view(
+ [('general-operation_search', forms.OperationFormSelection)],
label=_(u"Operation search"),
url_name='operation_search',)
wizard_steps = [
- ('filechoice-operation_creation', OperationFormFileChoice),
- ('general-operation_creation', OperationFormGeneral),
- ('judiciary-operation_creation', CourtOrderedSeizureForm),
- ('collaborators-operation_creation', CollaboratorForm),
- ('archaeologicalsite-operation_creation', ArchaeologicalSiteFormSet),
- ('preventive-operation_creation', OperationFormPreventive),
- ('preventivediag-operation_creation', OperationFormPreventiveDiag),
- ('townsgeneral-operation_creation', TownFormset),
- ('towns-operation_creation', SelectedTownFormset),
- ('parcelsgeneral-operation_creation', SelectedParcelGeneralFormSet),
- ('parcels-operation_creation', SelectedParcelFormSet),
- ('remains-operation_creation', RemainForm),
- ('periods-operation_creation', PeriodForm),
- ('relations-operation_creation', RecordRelationsFormSet),
- ('abstract-operation_creation', OperationFormAbstract),
+ ('filechoice-operation_creation', forms.OperationFormFileChoice),
+ ('general-operation_creation', forms.OperationFormGeneral),
+ ('judiciary-operation_creation', forms.CourtOrderedSeizureForm),
+ ('collaborators-operation_creation', forms.CollaboratorForm),
+ ('archaeologicalsite-operation_creation', forms.ArchaeologicalSiteFormSet),
+ ('preventive-operation_creation', forms.OperationFormPreventive),
+ ('preventivediag-operation_creation', forms.OperationFormPreventiveDiag),
+ ('townsgeneral-operation_creation', forms.TownFormset),
+ ('towns-operation_creation', forms.SelectedTownFormset),
+ ('parcelsgeneral-operation_creation', forms.SelectedParcelGeneralFormSet),
+ ('parcels-operation_creation', forms.SelectedParcelFormSet),
+ ('remains-operation_creation', forms.RemainForm),
+ ('periods-operation_creation', forms.PeriodForm),
+ ('relations-operation_creation', forms.RecordRelationsFormSet),
+ ('abstract-operation_creation', forms.OperationFormAbstract),
('final-operation_creation', FinalForm)]
@@ -224,49 +206,51 @@ check_files_for_operation = get_check_files_for_operation()
ope_crea_condition_dict = {
'filechoice-operation_creation': check_files_for_operation,
- 'judiciary-operation_creation': is_judiciary(
+ 'judiciary-operation_creation': wizards.is_judiciary(
'general-operation_creation', models.OperationType, 'operation_type',
),
'preventive-operation_creation':
get_check_files_for_operation(
- is_preventive('general-operation_creation', models.OperationType,
- 'operation_type', 'prev_excavation')),
+ wizards.is_preventive('general-operation_creation',
+ models.OperationType, 'operation_type',
+ 'prev_excavation')),
'preventivediag-operation_creation':
get_check_files_for_operation(
- is_preventive('general-operation_creation', models.OperationType,
- 'operation_type', 'arch_diagnostic')),
- 'townsgeneral-operation_creation': has_associated_file(
+ wizards.is_preventive('general-operation_creation',
+ models.OperationType, 'operation_type',
+ 'arch_diagnostic')),
+ 'townsgeneral-operation_creation': wizards.has_associated_file(
'filechoice-operation_creation', negate=True),
- 'towns-operation_creation': has_associated_file(
+ 'towns-operation_creation': wizards.has_associated_file(
'filechoice-operation_creation'),
- 'parcelsgeneral-operation_creation': has_associated_file(
+ 'parcelsgeneral-operation_creation': wizards.has_associated_file(
'filechoice-operation_creation', negate=True),
- 'parcels-operation_creation': has_associated_file(
+ 'parcels-operation_creation': wizards.has_associated_file(
'filechoice-operation_creation'),
}
-operation_creation_wizard = OperationWizard.as_view(
+operation_creation_wizard = wizards.OperationWizard.as_view(
wizard_steps,
label=_(u"New operation"),
condition_dict=ope_crea_condition_dict,
url_name='operation_creation',)
operation_modif_wizard_steps = [
- ('selec-operation_modification', OperationFormSelection),
- ('general-operation_modification', OperationFormModifGeneral),
- ('judiciary-operation_modification', CourtOrderedSeizureForm),
- ('collaborators-operation_modification', CollaboratorForm),
- ('archaeologicalsite-operation_modification', ArchaeologicalSiteFormSet),
- ('preventive-operation_modification', OperationFormPreventive),
- ('preventivediag-operation_modification', OperationFormPreventiveDiag),
- ('towns-operation_modification', SelectedTownFormset),
- ('townsgeneral-operation_modification', TownFormset),
- ('parcels-operation_modification', SelectedParcelFormSet),
- ('parcelsgeneral-operation_modification', SelectedParcelGeneralFormSet),
- ('remains-operation_modification', RemainForm),
- ('periods-operation_modification', PeriodForm),
- ('relations-operation_modification', RecordRelationsFormSet),
- ('abstract-operation_modification', OperationFormAbstract),
+ ('selec-operation_modification', forms.OperationFormSelection),
+ ('general-operation_modification', forms.OperationFormModifGeneral),
+ ('judiciary-operation_modification', forms.CourtOrderedSeizureForm),
+ ('collaborators-operation_modification', forms.CollaboratorForm),
+ ('archaeologicalsite-operation_modification', forms.ArchaeologicalSiteFormSet),
+ ('preventive-operation_modification', forms.OperationFormPreventive),
+ ('preventivediag-operation_modification', forms.OperationFormPreventiveDiag),
+ ('towns-operation_modification', forms.SelectedTownFormset),
+ ('townsgeneral-operation_modification', forms.TownFormset),
+ ('parcels-operation_modification', forms.SelectedParcelFormSet),
+ ('parcelsgeneral-operation_modification', forms.SelectedParcelGeneralFormSet),
+ ('remains-operation_modification', forms.RemainForm),
+ ('periods-operation_modification', forms.PeriodForm),
+ ('relations-operation_modification', forms.RecordRelationsFormSet),
+ ('abstract-operation_modification', forms.OperationFormAbstract),
('final-operation_modification', FinalForm)
]
@@ -274,27 +258,29 @@ operation_modif_wizard_steps = [
ope_modif_condition_dict = {
'preventive-operation_modification':
get_check_files_for_operation(
- is_preventive('general-operation_modification', models.OperationType,
- 'operation_type', 'prev_excavation')),
+ wizards.is_preventive('general-operation_modification',
+ models.OperationType, 'operation_type',
+ 'prev_excavation')),
'preventivediag-operation_modification':
get_check_files_for_operation(
- is_preventive('general-operation_modification', models.OperationType,
- 'operation_type', 'arch_diagnostic')),
- 'judiciary-operation_modification': is_judiciary(
+ wizards.is_preventive('general-operation_modification',
+ models.OperationType, 'operation_type',
+ 'arch_diagnostic')),
+ 'judiciary-operation_modification': wizards.is_judiciary(
'general-operation_modification', models.OperationType, 'operation_type',
),
- 'townsgeneral-operation_modification': has_associated_file(
+ 'townsgeneral-operation_modification': wizards.has_associated_file(
'general-operation_modification', negate=True),
- 'towns-operation_modification': has_associated_file(
+ 'towns-operation_modification': wizards.has_associated_file(
'general-operation_modification'),
- 'parcelsgeneral-operation_modification': has_associated_file(
+ 'parcelsgeneral-operation_modification': wizards.has_associated_file(
'general-operation_modification', negate=True),
- 'parcels-operation_modification': has_associated_file(
+ 'parcels-operation_modification': wizards.has_associated_file(
'general-operation_modification'),
}
-operation_modification_wizard = OperationModificationWizard.as_view(
+operation_modification_wizard = wizards.OperationModificationWizard.as_view(
operation_modif_wizard_steps,
label=_(u"Operation modification"),
condition_dict=ope_modif_condition_dict,
@@ -307,7 +293,7 @@ def operation_modify(request, pk):
return HttpResponseRedirect("/")
wizard_url = 'operation_modification'
- OperationModificationWizard.session_set_value(
+ wizards.OperationModificationWizard.session_set_value(
request, 'selec-' + wizard_url, 'pk', pk, reset=True)
return redirect(reverse(wizard_url,
kwargs={'step': 'general-' + wizard_url}))
@@ -315,29 +301,29 @@ def operation_modify(request, pk):
def operation_add(request, file_id):
operation_creation_wizard(request)
- OperationWizard.session_set_value(
+ wizards.OperationWizard.session_set_value(
request, 'filechoice-operation_creation', 'associated_file',
file_id, reset=True)
return redirect(reverse('operation_creation',
kwargs={'step': 'general-operation_creation'}))
operation_closing_steps = [
- ('selec-operation_closing', OperationFormSelection),
+ ('selec-operation_closing', forms.OperationFormSelection),
('date-operation_closing', ClosingDateFormSelection),
- ('final-operation_closing', FinalOperationClosingForm)]
+ ('final-operation_closing', forms.FinalOperationClosingForm)]
-operation_closing_wizard = OperationClosingWizard.as_view(
+operation_closing_wizard = wizards.OperationClosingWizard.as_view(
operation_closing_steps,
label=_(u"Operation closing"),
url_name='operation_closing',)
operation_deletion_steps = [
- ('selec-operation_deletion', OperationFormMultiSelection),
- ('final-operation_deletion', OperationDeletionForm)
+ ('selec-operation_deletion', forms.OperationFormMultiSelection),
+ ('final-operation_deletion', forms.OperationDeletionForm)
]
-operation_deletion_wizard = OperationDeletionWizard.as_view(
+operation_deletion_wizard = wizards.OperationDeletionWizard.as_view(
operation_deletion_steps,
label=_(u"Operation deletion"),
url_name='operation_deletion',)
@@ -349,7 +335,7 @@ def operation_delete(request, pk):
return HttpResponseRedirect("/")
wizard_url = 'operation_deletion'
- OperationDeletionWizard.session_set_value(
+ wizards.OperationDeletionWizard.session_set_value(
request, 'selec-' + wizard_url, 'pks', pk, reset=True)
return redirect(reverse(wizard_url,
kwargs={'step': 'final-' + wizard_url}))
@@ -362,43 +348,44 @@ def site_extra_context(request, item):
get_site = get_item(models.ArchaeologicalSite, 'get_site', 'site',
- search_form=SiteSelect)
+ search_form=forms.SiteSelect)
show_site = show_item(
models.ArchaeologicalSite, 'site',
extra_dct=site_extra_context
)
revert_site = revert_item(models.ArchaeologicalSite)
-site_search_wizard = SiteSearch.as_view(
- [('general-site_search', SiteFormSelection)],
+site_search_wizard = wizards.SiteSearch.as_view(
+ [('general-site_search', forms.SiteFormSelection)],
url_name='site_search',
)
site_creation_steps = [
- ('general-site_creation', SiteForm),
- ('towns-site_creation', SiteTownFormset),
- ('underwater-site_creation', SiteUnderwaterForm),
+ ('general-site_creation', forms.SiteForm),
+ ('towns-site_creation', forms.SiteTownFormset),
+ ('underwater-site_creation', forms.SiteUnderwaterForm),
('final-site_creation', FinalForm)
]
-site_creation_wizard = SiteWizard.as_view(
+site_creation_wizard = wizards.SiteWizard.as_view(
site_creation_steps,
- condition_dict={'underwater-site_creation': check_underwater_module},
+ condition_dict={'underwater-site_creation': forms.check_underwater_module},
url_name='site_creation',
)
site_modification_steps = [
- ('selec-site_modification', SiteFormSelection),
- ('general-site_modification', SiteForm),
- ('towns-site_modification', SiteTownFormset),
- ('underwater-site_modification', SiteUnderwaterForm),
+ ('selec-site_modification', forms.SiteFormSelection),
+ ('general-site_modification', forms.SiteForm),
+ ('towns-site_modification', forms.SiteTownFormset),
+ ('underwater-site_modification', forms.SiteUnderwaterForm),
('final-site_modification', FinalForm)
]
-site_modification_wizard = SiteModificationWizard.as_view(
+site_modification_wizard = wizards.SiteModificationWizard.as_view(
site_modification_steps,
- condition_dict={'underwater-site_modification': check_underwater_module},
+ condition_dict={
+ 'underwater-site_modification': forms.check_underwater_module},
url_name='site_modification',
)
@@ -408,18 +395,18 @@ def site_modify(request, pk):
models.ArchaeologicalSite, pk):
return HttpResponseRedirect("/")
wizard_url = 'site_modification'
- SiteModificationWizard.session_set_value(
+ wizards.SiteModificationWizard.session_set_value(
request, 'selec-' + wizard_url, 'pk', pk, reset=True)
return redirect(reverse(wizard_url,
kwargs={'step': 'general-' + wizard_url}))
site_deletion_steps = [
- ('selec-site_deletion', SiteFormMultiSelection),
+ ('selec-site_deletion', forms.SiteFormMultiSelection),
('final-site_deletion', FinalDeleteForm)
]
-site_deletion_wizard = SiteDeletionWizard.as_view(
+site_deletion_wizard = wizards.SiteDeletionWizard.as_view(
site_deletion_steps,
label=_(u"Site deletion"),
url_name='site_deletion',)
@@ -430,38 +417,38 @@ def site_delete(request, pk):
models.ArchaeologicalSite, pk):
return HttpResponseRedirect("/")
wizard_url = 'site_deletion'
- SiteDeletionWizard.session_set_value(
+ wizards.SiteDeletionWizard.session_set_value(
request, 'selec-' + wizard_url, 'pks', pk, reset=True)
return redirect(reverse(wizard_url,
kwargs={'step': 'final-' + wizard_url}))
-operation_administrativeactop_search_wizard = SearchWizard.as_view([
+operation_administrativeactop_search_wizard = wizards.SearchWizard.as_view([
('general-operation_administrativeactop_search',
- AdministrativeActOpeFormSelection)],
+ forms.AdministrativeActOpeFormSelection)],
label=_(u"Administrative act search"),
url_name='operation_administrativeactop_search',)
administrativeactop_steps = [
- ('selec-operation_administrativeactop', OperationFormSelection),
+ ('selec-operation_administrativeactop', forms.OperationFormSelection),
('administrativeact-operation_administrativeactop',
- AdministrativeActOpeForm),
+ forms.AdministrativeActOpeForm),
('final-operation_administrativeactop', FinalForm)]
operation_administrativeactop_wizard = \
- OperationAdministrativeActWizard.as_view(
+ wizards.OperationAdministrativeActWizard.as_view(
administrativeactop_steps,
label=_(u"Operation: new administrative act"),
url_name='operation_administrativeactop',)
operation_administrativeactop_modification_wizard = \
- OperationEditAdministrativeActWizard.as_view([
+ wizards.OperationEditAdministrativeActWizard.as_view([
('selec-operation_administrativeactop_modification',
- AdministrativeActOpeFormSelection),
+ forms.AdministrativeActOpeFormSelection),
('administrativeact-operation_administrativeactop_modification',
- AdministrativeActOpeModifForm),
+ forms.AdministrativeActOpeModifForm),
('final-operation_administrativeactop_modification', FinalForm)],
- label=_(u"Operation: administrative act modification"),
+ label=_("Operation: administrative act modification"),
url_name='operation_administrativeactop_modification',)
@@ -471,18 +458,18 @@ def operation_administrativeactop_modify(request, pk):
models.AdministrativeAct, pk):
return HttpResponseRedirect("/")
wizard_url = 'operation_administrativeactop_modification'
- OperationEditAdministrativeActWizard.session_set_value(
+ wizards.OperationEditAdministrativeActWizard.session_set_value(
request, 'selec-' + wizard_url, 'pk', pk, reset=True)
return redirect(
reverse(wizard_url, kwargs={'step': 'administrativeact-' + wizard_url}))
operation_administrativeactop_deletion_wizard = \
- AdministrativeActDeletionWizard.as_view([
+ wizards.AdministrativeActDeletionWizard.as_view([
('selec-operation_administrativeactop_deletion',
- AdministrativeActOpeFormSelection),
+ forms.AdministrativeActOpeFormSelection),
('final-operation_administrativeactop_deletion',
- FinalAdministrativeActDeleteForm)],
+ forms.FinalAdministrativeActDeleteForm)],
label=_(u"Operation: administrative act deletion"),
url_name='operation_administrativeactop_deletion',)
@@ -493,7 +480,7 @@ def operation_administrativeactop_delete(request, pk):
models.AdministrativeAct, pk):
return HttpResponseRedirect("/")
wizard_url = 'operation_administrativeactop_deletion'
- AdministrativeActDeletionWizard.session_set_value(
+ wizards.AdministrativeActDeletionWizard.session_set_value(
request, 'selec-' + wizard_url, 'pk', pk, reset=True)
return redirect(
reverse(wizard_url, kwargs={'step': 'final-' + wizard_url}))
@@ -501,7 +488,7 @@ def operation_administrativeactop_delete(request, pk):
administrativact_register_wizard = SearchWizard.as_view([
('general-administrativact_register',
- AdministrativeActRegisterFormSelection)],
+ forms.AdministrativeActRegisterFormSelection)],
label=pgettext_lazy('admin act register', u"Register"),
url_name='administrativact_register',)
@@ -511,7 +498,7 @@ generatedoc_administrativeactop = gen_generate_doc(models.AdministrativeAct)
def administrativeactfile_document(
request, file=False, treatment=False, treatment_file=False):
- search_form = AdministrativeActOpeFormSelection
+ search_form = forms.AdministrativeActOpeFormSelection
document_type = 'O'
if file:
from archaeological_files.forms import \
@@ -533,6 +520,7 @@ def administrativeactfile_document(
models.AdministrativeAct):
return HttpResponse(content_type='text/plain')
dct = {}
+ DocumentGenerationAdminActForm = forms.DocumentGenerationAdminActForm
if request.POST:
dct['search_form'] = search_form(request.POST)
dct['template_form'] = DocumentGenerationAdminActForm(
@@ -578,22 +566,22 @@ def administrativeactfile_document(
def reset_wizards(request):
for wizard_class, url_name in (
- (OperationWizard, 'operation_creation'),
- (OperationModificationWizard, 'operation_modification'),
- (OperationClosingWizard, 'operation_closing'),
- (OperationDeletionWizard, 'operation_deletion_wizard'),
- (OperationAdministrativeActWizard,
+ (wizards.OperationWizard, 'operation_creation'),
+ (wizards.OperationModificationWizard, 'operation_modification'),
+ (wizards.OperationClosingWizard, 'operation_closing'),
+ (wizards.OperationDeletionWizard, 'operation_deletion_wizard'),
+ (wizards.OperationAdministrativeActWizard,
'operation_administrativeactop'),
- (OperationEditAdministrativeActWizard,
+ (wizards.OperationEditAdministrativeActWizard,
'operation_administrativeactop_modification'),
- (AdministrativeActDeletionWizard,
+ (wizards.AdministrativeActDeletionWizard,
'operation_administrativeactop_deletion'),):
wizard_class.session_reset(request, url_name)
class QAOperationForm(QAItemEditForm):
model = models.Operation
- form_class = QAOperationFormMulti
+ form_class = forms.QAOperationFormMulti
class QAOperationLockView(QABaseLockView):
@@ -604,3 +592,50 @@ class QAOperationLockView(QABaseLockView):
class QASiteLockView(QABaseLockView):
model = models.ArchaeologicalSite
base_url = "site-qa-lock"
+
+
+class QAOperationdDuplicateFormView(QAItemForm):
+ template_name = 'ishtar/forms/qa_operation_duplicate.html'
+ model = models.Operation
+ page_name = _("Duplicate")
+ form_class = forms.QAOperationDuplicateForm
+ base_url = "operation-qa-duplicate"
+
+ def get_form_kwargs(self):
+ kwargs = super(QAOperationdDuplicateFormView, self).get_form_kwargs()
+ kwargs['user'] = self.request.user
+ return kwargs
+
+ def form_valid(self, form):
+ form.save()
+ return HttpResponseRedirect(reverse("success"))
+
+ def get_context_data(self, **kwargs):
+ data = super(QAOperationdDuplicateFormView, self).get_context_data(
+ **kwargs)
+ data['action_name'] = _("Duplicate")
+ return data
+
+
+class QAArchaeologicalSiteDuplicateFormView(QAItemForm):
+ template_name = 'ishtar/forms/qa_site_duplicate.html'
+ model = models.ArchaeologicalSite
+ page_name = _("Duplicate")
+ form_class = forms.QAArchaeologicalSiteDuplicateForm
+ base_url = "site-qa-duplicate"
+
+ def get_form_kwargs(self):
+ kwargs = super(QAArchaeologicalSiteDuplicateFormView,
+ self).get_form_kwargs()
+ kwargs['user'] = self.request.user
+ return kwargs
+
+ def form_valid(self, form):
+ form.save()
+ return HttpResponseRedirect(reverse("success"))
+
+ def get_context_data(self, **kwargs):
+ data = super(QAArchaeologicalSiteDuplicateFormView,
+ self).get_context_data(**kwargs)
+ data['action_name'] = _("Duplicate")
+ return data
diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py
index 7a6f2fe72..31eb5c7d2 100644
--- a/ishtar_common/forms_common.py
+++ b/ishtar_common/forms_common.py
@@ -1302,9 +1302,9 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType):
not cleaned_data.get('image', None) and \
not cleaned_data.get('associated_file', None) and \
not cleaned_data.get('associated_url', None):
- raise forms.ValidationError(_(u"You should at least fill one of "
- u"this field: title, url, image or "
- u"file."))
+ raise forms.ValidationError(_("You should at least fill one of "
+ "this field: title, url, image or "
+ "file."))
for rel in models.Document.RELATED_MODELS:
if cleaned_data.get(rel, None):
return cleaned_data
@@ -1477,6 +1477,68 @@ class QADocumentFormMulti(QAForm):
return value
+class QADocumentDuplicateForm(IshtarForm):
+ qa_title = forms.CharField(label=_("Reference"), max_length=500,
+ required=False)
+ qa_source_type = forms.ChoiceField(label=_("Type"), choices=[],
+ required=False)
+
+ TYPES = [
+ FieldType('qa_source_type', models.SourceType),
+ ]
+
+ def __init__(self, *args, **kwargs):
+ self.user = None
+ if 'user' in kwargs:
+ self.user = kwargs.pop('user')
+ if hasattr(self.user, 'ishtaruser'):
+ self.user = self.user.ishtaruser
+ self.document = kwargs.pop('items')[0]
+ super(QADocumentDuplicateForm, self).__init__(*args, **kwargs)
+
+ self.fields['qa_title'].initial = self.document.title + str(
+ _(" - duplicate"))
+ if self.document.source_type:
+ self.fields['qa_source_type'].initial = self.document.source_type.pk
+
+ for related_key in models.Document.RELATED_MODELS_ALT:
+ related = getattr(self.document, related_key)
+ if not related.count():
+ continue
+ model = models.Document._meta.get_field(related_key).related_model
+ initial = []
+ for item in related.all():
+ initial.append(item.pk)
+ self.fields["qa_" + related_key] = widgets.Select2MultipleField(
+ model=model, remote=True, label=model._meta.verbose_name_plural,
+ required=False, long_widget=True, initial=initial
+ )
+
+ def save(self):
+ data = {"index": None}
+ for k in ["title"]:
+ data[k] = self.cleaned_data.get("qa_" + k, None)
+ if self.cleaned_data.get("qa_source_type", None):
+ try:
+ data["source_type"] = models.SourceType.objects.get(
+ pk=int(self.cleaned_data["qa_source_type"]), available=True)
+ except models.SourceType.DoesNotExist:
+ return
+ new = self.document.duplicate_item(self.user, data=data)
+ for related_key in models.Document.RELATED_MODELS_ALT:
+ getattr(new, related_key).clear()
+ values = self.cleaned_data.get("qa_" + related_key, [])
+ model = models.Document._meta.get_field(related_key).related_model
+ for value in values:
+ getattr(new, related_key).add(model.objects.get(pk=value))
+ new.skip_history_when_saving = True
+ new._cached_label_checked = False
+ new._search_updated = False
+ new._no_move = True
+ new.save() # regen of labels
+ return new
+
+
class QALockForm(forms.Form):
action = forms.ChoiceField(
label=_("Action"), choices=(('lock', _("Lock")),
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index 7238d7782..dc2fef815 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -1212,7 +1212,7 @@ class HistoryError(Exception):
return repr(self.value)
-PRIVATE_FIELDS = ('id', 'history_modifier', 'order')
+PRIVATE_FIELDS = ('id', 'history_modifier', 'order', 'uuid')
class BulkUpdatedItem(object):
@@ -2127,6 +2127,34 @@ class CascasdeUpdate:
post_save_geo(item.__class__, instance=item)
+def duplicate_item(item, user=None, data=None):
+ model = item.__class__
+ new = model.objects.get(pk=item.pk)
+
+ for field in model._meta.fields:
+ # pk is in PRIVATE_FIELDS so: new.pk = None and a new
+ # item will be created on save
+ if field.name == "uuid":
+ new.uuid = uuid.uuid4()
+ elif field.name in PRIVATE_FIELDS:
+ setattr(new, field.name, None)
+ if user:
+ new.history_user = user
+ if data:
+ for k in data:
+ setattr(new, k, data[k])
+ new.save()
+
+ # m2m fields
+ m2m = [field.name for field in model._meta.many_to_many
+ if field.name not in PRIVATE_FIELDS]
+ for field in m2m:
+ for val in getattr(item, field).all():
+ if val not in getattr(new, field).all():
+ getattr(new, field).add(val)
+ return new
+
+
class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported,
JsonData, FixAssociated, CascasdeUpdate):
"""
@@ -2194,29 +2222,7 @@ class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported,
return {}
def duplicate(self, user=None, data=None):
- model = self.__class__
- new = model.objects.get(pk=self.pk)
-
- for field in model._meta.fields:
- # pk is in PRIVATE_FIELDS so: new.pk = None and a new
- # item will be created on save
- if field.name in PRIVATE_FIELDS:
- setattr(new, field.name, None)
- if user:
- new.history_user = user
- if data:
- for k in data:
- setattr(new, k, data[k])
- new.save()
-
- # m2m fields
- m2m = [field.name for field in model._meta.many_to_many
- if field.name not in PRIVATE_FIELDS]
- for field in m2m:
- for val in getattr(self, field).all():
- if val not in getattr(new, field).all():
- getattr(new, field).add(val)
- return new
+ return duplicate_item(self, user, data)
def update_external_id(self, save=False):
if not self.EXTERNAL_ID_KEY or (
@@ -5233,7 +5239,12 @@ class Document(BaseHistorizedItem, QRCodeItem, OwnPerms, ImageModel,
text=_(u"Bulk update"), target="many",
rights=['change_document', 'change_own_document'])
QUICK_ACTIONS = [
- QA_EDIT
+ QA_EDIT,
+ QuickAction(
+ url="document-qa-duplicate", icon_class="fa fa-clone",
+ text=_("Duplicate"), target="one",
+ rights=['change_document',
+ 'change_own_document']),
]
SERIALIZATION_FILES = ["image", "thumbnail", "associated_file"]
@@ -5321,6 +5332,8 @@ class Document(BaseHistorizedItem, QRCodeItem, OwnPerms, ImageModel,
return "{}-{:04d}".format(self.operation.code_patriarche or '',
self.index)
"""
+ def duplicate_item(self, user=None, data=None):
+ return duplicate_item(self, user, data)
def public_representation(self):
site = Site.objects.get_current()
@@ -5352,6 +5365,22 @@ class Document(BaseHistorizedItem, QRCodeItem, OwnPerms, ImageModel,
"thumbnail": thumbnail,
}
+ def get_extra_actions(self, request):
+ """
+ For sheet template
+ """
+ # url, base_text, icon, extra_text, extra css class, is a quick action
+ actions = super(Document, self).get_extra_actions(request)
+ # is_locked = self.is_locked(request.user)
+
+ can_edit_document = self.can_do(request, 'change_document')
+ if can_edit_document:
+ actions += [
+ (reverse("document-qa-duplicate", args=[self.pk]),
+ _("Duplicate"), "fa fa-clone", "", "", True),
+ ]
+ return actions
+
@property
def thumbnail_path(self):
if not self.thumbnail:
diff --git a/ishtar_common/templates/ishtar/forms/qa_document_duplicate.html b/ishtar_common/templates/ishtar/forms/qa_document_duplicate.html
new file mode 100644
index 000000000..4dc04d15d
--- /dev/null
+++ b/ishtar_common/templates/ishtar/forms/qa_document_duplicate.html
@@ -0,0 +1,95 @@
+{% extends "ishtar/forms/qa_base.html" %}
+{% load i18n inline_formset table_form %}
+
+{% block main_form %}
+{% if form.non_field_errors %}
+<div class="alert alert-danger" role="alert">
+ {{form.non_field_errors}}
+</div>
+{% endif %}
+{% with force_large_col=True %}
+<div class="form-row">
+ {% with form.qa_title as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+<div class="form-row">
+ {% with form.qa_source_type as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endwith %}
+
+{% if form.qa_finds %}
+<div class="form-row">
+ {% with form.qa_finds as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endif %}
+
+{% if form.qa_context_records %}
+<div class="form-row">
+ {% with form.qa_context_records as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endif %}
+
+{% if form.qa_operations %}
+<div class="form-row">
+ {% with form.qa_operations as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endif %}
+
+{% if form.qa_sites %}
+<div class="form-row">
+ {% with form.qa_sites as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endif %}
+
+{% if form.qa_files %}
+<div class="form-row">
+ {% with form.qa_files as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endif %}
+
+{% if form.qa_warehouses %}
+<div class="form-row">
+ {% with form.qa_warehouses as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endif %}
+
+{% if form.qa_containers %}
+<div class="form-row">
+ {% with form.qa_containers as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endif %}
+
+{% if form.qa_treatments %}
+<div class="form-row">
+ {% with form.qa_treatments as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endif %}
+
+{% if form.qa_files %}
+<div class="form-row">
+ {% with form.qa_treatment_files as field %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endwith %}
+</div>
+{% endif %}
+
+{% endblock %}
diff --git a/ishtar_common/templates/ishtar/sheet_document.html b/ishtar_common/templates/ishtar/sheet_document.html
index 386b33a8a..93951c056 100644
--- a/ishtar_common/templates/ishtar/sheet_document.html
+++ b/ishtar_common/templates/ishtar/sheet_document.html
@@ -37,7 +37,7 @@
{% field_flex "Creation date" item.creation_date %}
{% field_flex "Receipt date" item.receipt_date %}
{% field_flex "Receipt date in documentation" item.receipt_date_in_documentation %}
- {% field_flex "Has a duplicate" item.duplicate %}
+ {% if item.duplicate %}{% field_flex "Has a duplicate" item.duplicate %}{% endif %}
{% field_flex "Description" item.description %}
{% field_flex "Comment" item.comment %}
{% field_flex "Additional information" item.additional_information %}
diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py
index 8c08ade06..cc8d3eed4 100644
--- a/ishtar_common/urls.py
+++ b/ishtar_common/urls.py
@@ -319,6 +319,10 @@ urlpatterns += [
check_rights(['change_document', 'change_own_document'])(
views.QADocumentForm.as_view()),
name='document-qa-bulk-update-confirm', kwargs={"confirm": True}),
+ url(r'^document-qa-duplicate/(?P<pks>[0-9-]+)?/$',
+ check_rights(['change_document', 'change_own_document'])(
+ views.QADocumentDuplicateFormView.as_view()),
+ name='document-qa-duplicate'),
url(r'^qa-not-available(?:/(?P<context>[0-9a-z-]+))?/$',
views.QANotAvailable.as_view(), name='qa-not-available'),
diff --git a/ishtar_common/views.py b/ishtar_common/views.py
index 3563ac9db..4724d61b7 100644
--- a/ishtar_common/views.py
+++ b/ishtar_common/views.py
@@ -2264,6 +2264,8 @@ class QAItemForm(IshtarMixin, LoginRequiredMixin, FormView):
# check availability
quick_action = self.get_quick_action()
+ if not quick_action:
+ raise Http404()
if not quick_action.is_available(
user=request.user, session=request.session):
for item in self.items:
@@ -2379,3 +2381,27 @@ class QADocumentForm(QAItemEditForm):
form_class = forms.QADocumentFormMulti
+class QADocumentDuplicateFormView(QAItemForm):
+ template_name = 'ishtar/forms/qa_document_duplicate.html'
+ model = models.Document
+ page_name = _("Duplicate")
+ form_class = forms.QADocumentDuplicateForm
+ base_url = "document-qa-duplicate"
+
+ def get_form_kwargs(self):
+ kwargs = super(QADocumentDuplicateFormView,
+ self).get_form_kwargs()
+ kwargs['user'] = self.request.user
+ return kwargs
+
+ def form_valid(self, form):
+ form.save()
+ return HttpResponseRedirect(reverse("success"))
+
+ def get_context_data(self, **kwargs):
+ data = super(QADocumentDuplicateFormView,
+ self).get_context_data(**kwargs)
+ data['action_name'] = _("Duplicate")
+ return data
+
+
diff --git a/scss/custom.scss b/scss/custom.scss
index b00664552..4b0e4dfb8 100644
--- a/scss/custom.scss
+++ b/scss/custom.scss
@@ -116,6 +116,10 @@ pre {
border: $input-border-width solid $input-border-color;
}
+.modal-dialog .select2-selection{
+ width: 470px;
+}
+
.page-link.imported-page {
color: $ishtar-light-color;
}