diff options
| -rw-r--r-- | archaeological_finds/forms.py | 25 | ||||
| -rw-r--r-- | archaeological_finds/models_finds.py | 26 | ||||
| -rw-r--r-- | archaeological_finds/templates/ishtar/forms/qa_find_duplicate.html | 46 | ||||
| -rw-r--r-- | archaeological_finds/tests.py | 36 | ||||
| -rw-r--r-- | archaeological_finds/urls.py | 4 | ||||
| -rw-r--r-- | archaeological_finds/views.py | 29 | ||||
| -rw-r--r-- | ishtar_common/models.py | 25 | 
7 files changed, 187 insertions, 4 deletions
| diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py index 58c10971a..129a1446e 100644 --- a/archaeological_finds/forms.py +++ b/archaeological_finds/forms.py @@ -726,6 +726,31 @@ class QAFindBasketForm(IshtarForm):              basket.items.add(item) +class QAFindDuplicateForm(IshtarForm): +    label = forms.CharField(label=_("Free ID"), max_length=None, required=True) +    denomination = forms.CharField(label=_("Denomination"), max_length=None, +                                   required=False) +    # modify = forms.BooleanField(label=_("Edit the new find"), 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.find = kwargs.pop('items')[0] +        super(QAFindDuplicateForm, self).__init__(*args, **kwargs) +        self.fields['label'].initial = self.find.label + str( +            _(u" - duplicate")) +        self.fields['denomination'].initial = self.find.denomination or "" + +    def save(self): +        return self.find.duplicate( +            self.user, duplicate_for_treatment=False, +            data={"label": self.cleaned_data["label"], +                  "denomination": self.cleaned_data["denomination"]}) + +  class QAFindbasketDuplicateForm(IshtarForm):      label = forms.CharField(label="", max_length=None, required=True) diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index ad9e2a822..4f28b977c 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -1385,6 +1385,10 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,      QUICK_ACTIONS = [          QA_EDIT,          QuickAction( +            url="find-qa-duplicate", icon_class="fa fa-clone", +            text=_(u"Duplicate"), target="one", +            rights=['change_find', 'change_own_find']), +        QuickAction(              url="find-qa-basket", icon_class="fa fa-shopping-basket",              text=_(u"Basket"), target="many",              rights=['change_find', 'change_own_find']), @@ -1686,6 +1690,8 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,          can_edit_find = self.can_do(request, 'change_find')          if can_edit_find:              actions += [ +                (reverse("find-qa-duplicate", args=[self.pk]), +                 _("Duplicate"), "fa fa-clone", "", "", True),                  (reverse("find-qa-basket", args=[self.pk]),                   _(u"Add to basket"),                   "fa fa-shopping-basket", "", "", True), @@ -1873,13 +1879,14 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,              base_finds__context_record__operation__pk=operation_id)      @classmethod -    def get_total_number(cls, fltr={}): +    def get_total_number(cls, fltr=None):          q = cls.objects          if fltr:              q = q.filter(**fltr)          return q.filter(downstream_treatment__isnull=True).count() -    def duplicate(self, user, copy_datings=True): +    def duplicate(self, user, copy_datings=True, duplicate_for_treatment=True, +                  data=None):          model = self.__class__          new = model.objects.get(pk=self.pk) @@ -1889,8 +1896,13 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,              # item will be created on save              if field.name in PRIVATE_FIELDS:                  setattr(new, field.name, None) -        new.order = self.order + 1 -        new.history_order = user +        new.order = self.order +        if duplicate_for_treatment: +            new.order += 1 +        new.history_user = user +        if data: +            for k in data: +                setattr(new, k, data[k])          new.save()          # m2m fields @@ -1913,6 +1925,12 @@ class Find(BulkUpdatedItem, ValueGetter, DocumentItem, BaseHistorizedItem,                  for val in getattr(self, field).all():                      if val not in getattr(new, field).all():                          getattr(new, field).add(val) +        if not duplicate_for_treatment: +            bf = self.get_first_base_find() +            new.base_finds.clear() +            if bf: +                new.base_finds.add(bf.duplicate( +                    user=user, data={"label": new.label, "external_id": None}))          return new      @classmethod diff --git a/archaeological_finds/templates/ishtar/forms/qa_find_duplicate.html b/archaeological_finds/templates/ishtar/forms/qa_find_duplicate.html new file mode 100644 index 000000000..c00c74c31 --- /dev/null +++ b/archaeological_finds/templates/ishtar/forms/qa_find_duplicate.html @@ -0,0 +1,46 @@ +{% 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 %} +{% if operation %} +<div class="alert alert-info"> +    {% trans "The new find will have a new base find created. Instead of this action use a division treatment if the new find is from the same base find (share the same field data)." %} +</div> +<div class="form-row"> +    <div class="form-group col-lg-12 required full-width"> +        <label>{% trans "Operation" %}{% trans ":" %}</label> +        {{operation}} +    </div> +</div> +<div class="form-row"> +    <div class="form-group col-lg-12 required full-width"> +        <label>{% trans "Context record" %}{% trans ":" %}</label> +        {{context_record }} +    </div> +</div> +{% endif %} +{% with force_large_col=True %} +<div class="form-row"> +    {% with form.label as field %} +    {% include "blocks/bs_field_snippet.html" %} +    {% endwith %} +</div> +<div class="form-row"> +    {% with form.denomination 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_finds/tests.py b/archaeological_finds/tests.py index 806696d4c..acd86727b 100644 --- a/archaeological_finds/tests.py +++ b/archaeological_finds/tests.py @@ -1084,6 +1084,42 @@ class FindQATest(FindInit, TestCase):          self.alt_user.user_permissions.add(Permission.objects.get(              codename='change_find')) +    def test_duplicate(self): +        find = self.finds[0] +        default_desc = "Description for duplicate" +        find.description = default_desc +        find.save() +        c = Client() +        url = reverse('find-qa-duplicate', args=[find.pk]) +        response = c.get(url) +        self.assertRedirects(response, '/') + +        c = Client() +        c.login(username=self.username, password=self.password) +        response = c.get(url) +        self.assertEqual(response.status_code, 200) + +        c = Client() +        c.login(username=self.alt_username, password=self.alt_password) +        response = c.get(url) +        self.assertEqual(response.status_code, 200) + +        data = { +            "denomination": "clone", +            "label": "clone - free id" +        } +        nb_find = models.Find.objects.count() +        nb_bf = models.BaseFind.objects.count() +        response = c.post(url, data) +        self.assertEqual(response.status_code, 302)  # success redirect +        self.assertEqual(models.Find.objects.count(), nb_find + 1) +        self.assertEqual(models.BaseFind.objects.count(), nb_bf + 1) +        new = models.Find.objects.order_by('-pk').all()[0] +        self.assertEqual(new.description, default_desc) +        new_bf = models.BaseFind.objects.order_by('-pk').all()[0] +        base_bf = find.get_first_base_find() +        self.assertEqual(new_bf.context_record, base_bf.context_record) +      def test_bulk_update(self):          c = Client()          pks = u"{}-{}".format(self.finds[0].pk, self.finds[1].pk) diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py index 7b70f2296..0f86a363e 100644 --- a/archaeological_finds/urls.py +++ b/archaeological_finds/urls.py @@ -43,6 +43,10 @@ urlpatterns = [              views.find_deletion_wizard), name='find_deletion'),      url(r'find_modify/(?P<pk>.+)/$',          views.find_modify, name='find_modify'), +    url(r'^find-qa-duplicate/(?P<pks>[0-9-]+)?/$', +        check_rights(['change_find', 'change_own_find'])( +            views.QAFindDuplicateFormView.as_view()), +        name='find-qa-duplicate'),      url(r'get-findbasket/$', views.get_find_basket,          name='get-findbasket'),      url(r'get-findbasket-write/$', views.get_find_basket_for_write, diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index b8a23367b..ed0389cff 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -936,6 +936,35 @@ class QAFindBasketFormView(QAItemForm):          return HttpResponseRedirect(reverse("success")) +class QAFindDuplicateFormView(QAItemForm): +    template_name = 'ishtar/forms/qa_find_duplicate.html' +    model = models.Find +    page_name = _(u"Duplicate") +    form_class = forms.QAFindDuplicateForm + +    def get_quick_action(self): +        return models.Find.QUICK_ACTIONS[1] + +    def get_form_kwargs(self): +        kwargs = super(QAFindDuplicateFormView, 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(QAFindDuplicateFormView, self).get_context_data( +            **kwargs) +        data['action_name'] = _(u"Duplicate") +        bf = self.items[0].get_first_base_find() +        if bf: +            data['context_record'] = bf.context_record +            data['operation'] = bf.context_record.operation +        return data + +  class QAFindTreatmentFormView(QAItemForm):      template_name = 'ishtar/forms/qa_find_treatment.html'      model = models.Find diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 19be62948..1c841a984 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -1993,6 +1993,31 @@ class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported,      def merge(self, item, keep_old=False):          merge_model_objects(self, item, keep_old=keep_old) +    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 +      def update_external_id(self, save=False):          if not self.EXTERNAL_ID_KEY or (                  self.external_id and | 
