From afd7fb9c2be01a44a45f582eebdf02632a10be99 Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Thu, 29 Nov 2018 19:34:31 +0100 Subject: Basket: manage basket share (ro and edit) - manage alt query own --- ishtar_common/models.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'ishtar_common/models.py') diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 1aa94836f..1a0d80ac3 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -254,7 +254,7 @@ class OwnPerms(object): action_own_name, request.session) and self.is_own(request.user.ishtaruser)) - def is_own(self, user): + def is_own(self, user, alt_query_own=None): """ Check if the current object is owned by the user """ @@ -264,7 +264,10 @@ class OwnPerms(object): ishtaruser = user.ishtaruser else: return False - query = self.get_query_owns(ishtaruser) + if not alt_query_own: + query = self.get_query_owns(ishtaruser) + else: + query = getattr(self, alt_query_own)(ishtaruser) if not query: return False query &= Q(pk=self.pk) @@ -3293,6 +3296,7 @@ post_save.connect(post_save_userprofile, sender=UserProfile) class IshtarUser(FullSearch): + SLUG = "ishtaruser" TABLE_COLS = ('username', 'person__name', 'person__surname', 'person__email', 'person__person_types_list', 'person__attached_to__name') @@ -3441,9 +3445,13 @@ class Basket(FullSearch): verbose_name=_(u"Owner")) available = models.BooleanField(_(u"Available"), default=True) shared_with = models.ManyToManyField( - IshtarUser, verbose_name=_(u"Shared with"), blank=True, + IshtarUser, verbose_name=_(u"Shared (read) with"), blank=True, related_name='shared_%(class)ss' ) + shared_write_with = models.ManyToManyField( + IshtarUser, verbose_name=_(u"Shared (read/edit) with"), blank=True, + related_name='shared_write_%(class)ss' + ) TABLE_COLS = ['label', 'user'] @@ -3462,7 +3470,8 @@ class Basket(FullSearch): if not request.user or not getattr(request.user, 'ishtaruser', None): return Q(pk=None) ishtaruser = request.user.ishtaruser - return Q(user=ishtaruser) | Q(shared_with=ishtaruser) + return Q(user=ishtaruser) | Q(shared_with=ishtaruser) | Q( + shared_write_with=ishtaruser) @property def cached_label(self): -- cgit v1.2.3 From eddd946150bcd9d4ddb05000806cc2cdfb9af60d Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Fri, 30 Nov 2018 18:47:14 +0100 Subject: Manage basket to treatment file association --- archaeological_finds/forms.py | 21 +------ archaeological_finds/forms_treatments.py | 12 +++- .../migrations/0043_auto_20181130_1310.py | 34 +++++++++++ archaeological_finds/models_finds.py | 12 +--- archaeological_finds/models_treatments.py | 6 +- .../templates/ishtar/sheet_treatmentfile.html | 1 + .../ishtar/wizard/wizard_findbasket_deletion.html | 18 ++++++ .../ishtar/wizard/wizard_treatement_deletion.html | 4 +- archaeological_finds/tests.py | 3 +- archaeological_finds/urls.py | 10 +++- archaeological_finds/views.py | 67 ++++++++++------------ archaeological_finds/wizards.py | 5 ++ ishtar_common/forms_common.py | 2 +- ishtar_common/models.py | 31 +++++++++- ishtar_common/views_item.py | 24 ++++++-- 15 files changed, 164 insertions(+), 86 deletions(-) create mode 100644 archaeological_finds/migrations/0043_auto_20181130_1310.py create mode 100644 archaeological_finds/templates/ishtar/wizard/wizard_findbasket_deletion.html (limited to 'ishtar_common/models.py') diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py index 811e71a60..f78b4a5f3 100644 --- a/archaeological_finds/forms.py +++ b/archaeological_finds/forms.py @@ -82,7 +82,7 @@ __all__ = [ 'check_value', 'check_type_field', 'check_type_not_field', 'check_treatment', 'ResultFindForm', 'ResultFindFormSet', 'FindDeletionForm', 'UpstreamFindFormSelection', 'NewFindBasketForm', - 'SelectFindBasketForm', 'DeleteFindBasketForm', 'FindBasketAddItemForm', + 'SelectFindBasketForm', 'FindBasketAddItemForm', 'QAFindFormSingle', 'QAFindFormMulti', 'QAFindBasketForm', 'QAFindTreatmentForm', 'N1TreatmentForm', 'OneNTreatmentForm', 'ResultingFindForm', @@ -1268,25 +1268,6 @@ class SelectFindBasketForm(IshtarForm): ] -class DeleteFindBasketForm(SelectFindBasketForm): - def get_basket_choices(self): - return [('', u'--')] + [ - (str(b.pk), unicode(b)) - for b in models.FindBasket.objects.filter( - Q(user=self.user) - ) - ] - - def save(self): - try: - models.FindBasket.objects.get(pk=self.cleaned_data['basket'], - user=self.user).delete() - except models.FindBasket.DoesNotExist: - # something strange... TODO: log it - pass - return - - class FindBasketAddItemForm(forms.Form): basket_id = forms.IntegerField(required=True) item_id = forms.IntegerField(required=True) diff --git a/archaeological_finds/forms_treatments.py b/archaeological_finds/forms_treatments.py index be53dd418..4f120adbf 100644 --- a/archaeological_finds/forms_treatments.py +++ b/archaeological_finds/forms_treatments.py @@ -26,7 +26,7 @@ from django.core import validators from django.forms.formsets import formset_factory from django.utils.translation import ugettext_lazy as _ -import models +from archaeological_finds import models from archaeological_operations.forms import AdministrativeActForm, \ AdministrativeActOpeFormSelection, AdministrativeActModifForm from archaeological_operations.models import ActType, AdministrativeAct @@ -125,7 +125,6 @@ class BaseTreatmentForm(CustomForm, ManageOldType): max_length=200, required=False) other_reference = forms.CharField( label=_(u"Other ref."), max_length=200, required=False) - # target_is_basket = forms.NullBooleanField(label=_(u"Target")) # external_id = forms.CharField( # label=_(u"External ref."), max_length=200, required=False) start_date = forms.DateField(label=_(u"Start date"), required=False, @@ -570,7 +569,9 @@ class TreatmentFileForm(ManageOldType): base_models = ['treatment_type_type'] associated_models = { 'type': models.TreatmentFileType, 'in_charge': Person, - 'applicant': Person, 'applicant_organisation': Organization} + 'applicant': Person, 'applicant_organisation': Organization, + 'associated_basket': models.FindBasket + } need_user_for_initialization = True name = forms.CharField(label=_(u"Name"), @@ -603,6 +604,11 @@ class TreatmentFileForm(ManageOldType): reverse_lazy('autocomplete-organization'), associated_model=Organization, new=True), validators=[valid_id(Organization)], required=False) + associated_basket = forms.IntegerField( + _(u"Associated basket"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-findbasket'), + associated_model=models.FindBasket), required=False) comment = forms.CharField(label=_(u"Comment"), widget=forms.Textarea, required=False) creation_date = forms.DateField(label=_(u"Start date"), required=False, diff --git a/archaeological_finds/migrations/0043_auto_20181130_1310.py b/archaeological_finds/migrations/0043_auto_20181130_1310.py new file mode 100644 index 000000000..e8881e45d --- /dev/null +++ b/archaeological_finds/migrations/0043_auto_20181130_1310.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-11-30 13:10 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_finds', '0042_auto_20181129_1755'), + ] + + operations = [ + migrations.RemoveField( + model_name='historicaltreatment', + name='target_is_basket', + ), + migrations.RemoveField( + model_name='treatment', + name='target_is_basket', + ), + migrations.AddField( + model_name='historicaltreatmentfile', + name='associated_basket', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_finds.FindBasket'), + ), + migrations.AddField( + model_name='treatmentfile', + name='associated_basket', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_finds.FindBasket'), + ), + ] diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 0c9770186..75ea69ac1 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -580,7 +580,8 @@ WEIGHT_UNIT = (('g', _(u"g")), ('kg', _(u"kg")),) -class FindBasket(Basket, OwnPerms): +class FindBasket(Basket): + SHOW_URL = 'show-findbasket' items = models.ManyToManyField('Find', blank=True, related_name='basket') class Meta: @@ -589,15 +590,6 @@ class FindBasket(Basket, OwnPerms): ("view_own_find", u"Can view own Find"), ) - @classmethod - def get_query_owns(cls, ishtaruser): - return Q(user=ishtaruser) | Q(shared_with=ishtaruser) | Q( - shared_write_with=ishtaruser) - - @classmethod - def get_write_query_owns(cls, ishtaruser): - return Q(user=ishtaruser) - def get_extra_actions(self, request): """ For sheet template: return "Manage basket" action diff --git a/archaeological_finds/models_treatments.py b/archaeological_finds/models_treatments.py index 29afc94e3..cc0d17dd0 100644 --- a/archaeological_finds/models_treatments.py +++ b/archaeological_finds/models_treatments.py @@ -162,8 +162,6 @@ class Treatment(DashboardFormItem, ValueGetter, BaseHistorizedItem, blank=True, null=True) insurance_cost = models.FloatField(_(u"Insurance cost"), blank=True, null=True) - target_is_basket = models.BooleanField(_(u"Target a basket"), - default=False) documents = models.ManyToManyField( Document, related_name='treatments', verbose_name=_(u"Documents"), blank=True) @@ -766,6 +764,10 @@ class TreatmentFile(DashboardFormItem, ClosedItem, BaseHistorizedItem, documents = models.ManyToManyField( Document, related_name='treatment_files', verbose_name=_(u"Documents"), blank=True) + associated_basket = models.ForeignKey( + FindBasket, null=True, blank=True, on_delete=models.SET_NULL, + related_name='treatment_files' + ) cached_label = models.TextField(_(u"Cached name"), null=True, blank=True, db_index=True) history = HistoricalRecords() diff --git a/archaeological_finds/templates/ishtar/sheet_treatmentfile.html b/archaeological_finds/templates/ishtar/sheet_treatmentfile.html index 08398a6c2..072285262 100644 --- a/archaeological_finds/templates/ishtar/sheet_treatmentfile.html +++ b/archaeological_finds/templates/ishtar/sheet_treatmentfile.html @@ -34,6 +34,7 @@
{% field_flex "Type" item.type %} {% field_flex_detail "Responsible" item.in_charge %} + {% field_flex_detail "Associated basket" item.associated_basket %} {% field_flex "Creation date" item.creation_date %} {% field_flex "Reception date" item.reception_date %} {% field_flex "Closing date" item.end_date %} diff --git a/archaeological_finds/templates/ishtar/wizard/wizard_findbasket_deletion.html b/archaeological_finds/templates/ishtar/wizard/wizard_findbasket_deletion.html new file mode 100644 index 000000000..02f7253d7 --- /dev/null +++ b/archaeological_finds/templates/ishtar/wizard/wizard_findbasket_deletion.html @@ -0,0 +1,18 @@ +{% extends "ishtar/wizard/confirm_wizard.html" %} +{% load i18n link_to_window %} + +{% block "warning_message" %} +{% if current_object.treatment_files.count %} +
+ + {% trans "This basket is attached to treatments requests:" %} +
    {% for tf in current_object.treatment_files.all %} +
  • {{ tf }} {{tf|link_to_window}}
  • + {% endfor %}
+ {% trans "Are you sure you want to delete this basket?" %} +
+{% endif %} +
+ {% trans "Basket informations:" %} +
+{% endblock %} diff --git a/archaeological_finds/templates/ishtar/wizard/wizard_treatement_deletion.html b/archaeological_finds/templates/ishtar/wizard/wizard_treatement_deletion.html index b0ebe7409..be46bfd05 100644 --- a/archaeological_finds/templates/ishtar/wizard/wizard_treatement_deletion.html +++ b/archaeological_finds/templates/ishtar/wizard/wizard_treatement_deletion.html @@ -1,5 +1,5 @@ {% extends "ishtar/wizard/confirm_wizard.html" %} -{% load i18n %} +{% load i18n link_to_window %} {% block "warning_message" %} {% with has_downstream=current_object.downstream.count %} @@ -10,7 +10,7 @@ {% trans "The following finds will be deleted and restored to a previous version."%}
    {% for item in current_object.downstream.all %}
  • - {{item}} + {{item}} {{item|link_to_window}}
  • {% endfor %}
{% trans "All changes made to the associated finds since this treatment record will be lost!" %} diff --git a/archaeological_finds/tests.py b/archaeological_finds/tests.py index 8d1ffe91d..1f26463ca 100644 --- a/archaeological_finds/tests.py +++ b/archaeological_finds/tests.py @@ -215,8 +215,7 @@ class TreatmentWizardCreationTest(WizardTest, FindInit, TestCase): 'treatment_type': None, 'person': 1, # doer 'location': 1, # associated warehouse - 'year': 2016, - 'target_is_basket': False + 'year': 2016 }, 'selecfind': { 'pk': 1, diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py index 0ef3bac89..9f8776e62 100644 --- a/archaeological_finds/urls.py +++ b/archaeological_finds/urls.py @@ -80,10 +80,10 @@ urlpatterns = [ check_rights(['view_find', 'view_own_find'])( views.FindBasketListView.as_view()), name='list_iteminbasket'), - url(r'^find_basket_deletion/$', + url(r'^find_basket_deletion/(?P.+)?$', check_rights(['view_find', 'view_own_find'])( - views.DeleteFindBasketView.as_view()), name='delete_findbasket'), - + views.basket_delete_wizard), + name='find_basket_deletion'), url(r'^find-qa-bulk-update/(?P[0-9-]+)?/$', check_rights(['change_find', 'change_own_find'])( views.QAFindForm.as_view()), @@ -256,6 +256,10 @@ urlpatterns = [ administrativeactfile_document, name='treatmentfle-administrativeact-document', kwargs={'treatment_file': True}), + url(r'autocomplete-findbasket/$', + check_rights(['change_find', 'change_own_find'])( + views.autocomplete_findbasket), + name='autocomplete-findbasket'), ] urlpatterns += get_urls_for_model(models.Find, views, own=True, diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index 125567044..28c9495a3 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -37,7 +37,7 @@ from ishtar_common.models import IshtarUser, get_current_profile from ishtar_common.views import get_autocomplete_generic, IshtarMixin, \ LoginRequiredMixin, QAItemEditForm, QAItemForm from ishtar_common.views_item import display_item, get_item, show_item, \ - revert_item, get_autocomplete_item + revert_item, get_autocomplete_item, get_autocomplete_query from wizards import * get_find = get_item(models.Find, 'get_find', 'find') @@ -114,6 +114,23 @@ show_findbasket = show_item(models.FindBasket, 'findbasket', display_findbasket = display_item(models.FindBasket, show_url='show-find/basket-') + +def autocomplete_findbasket(request, current_right=None): + if not request.GET.get('term'): + return HttpResponse(content_type='text/plain') + + query = get_autocomplete_query(request, ['label']) + limit = 20 + query = query & models.FindBasket.get_write_query_owns( + request.user.ishtaruser) + items = models.FindBasket.objects.filter(query).order_by('label')[:limit] + data = json.dumps( + [{'id': item.pk, + 'value': u"{} - {}".format(item.label, item.user)[:60]} + for item in items] + ) + return HttpResponse(data, content_type='text/plain') + get_find_basket = get_item( models.FindBasket, 'get_findbasket', 'findbasket', model_for_perms=models.Find @@ -151,6 +168,19 @@ def find_basket_modify(request, pk): kwargs={'step': 'basket-find_basket_modification'})) +findbasket_deletion_steps = [ + ('selec-find_basket_deletion', FindBasketForWriteFormSelection), + ('final-find_basket_deletion', FinalForm) +] + + +basket_delete_wizard = FindBasketDeletionWizard.as_view( + findbasket_deletion_steps, + label=_(u"Basket deletion"), + url_name='find_basket_deletion', +) + + def check_preservation_module(self): return get_current_profile().preservation @@ -410,22 +440,6 @@ class FindBasketDeleteItemView(OwnBasket, IshtarMixin, LoginRequiredMixin, basket.items.remove(find) return HttpResponseRedirect(self.get_success_url(basket)) - -class DeleteFindBasketView(IshtarMixin, LoginRequiredMixin, FormView): - template_name = 'ishtar/form_delete.html' - form_class = DeleteFindBasketForm - success_url = '/' - page_name = _(u"Delete basket") - - def get_form_kwargs(self): - kwargs = super(DeleteFindBasketView, self).get_form_kwargs() - kwargs['user'] = IshtarUser.objects.get(pk=self.request.user.pk) - return kwargs - - def form_valid(self, form): - form.save() - return HttpResponseRedirect(self.get_success_url()) - get_upstreamtreatment = get_item( models.FindUpstreamTreatments, 'get_upstreamtreatment', 'uptreatment') @@ -446,25 +460,6 @@ treatment_search_wizard = TreatmentSearch.as_view([ label=_(u"Treatment search"), url_name='treatment_search',) -""" - condition_dict={ - 'selecfind-treatment_creation': - check_value('basetreatment-treatment_creation', - 'target_is_basket', False), - 'selecbasket-treatment_creation': - check_value('basetreatment-treatment_creation', - 'target_is_basket', True), - # 'resultfinds-treatment_creation': - # check_type_field('basetreatment-treatment_creation', - # 'treatment_type', models.TreatmentType, - # 'downstream_is_many'), - # 'resultfind-treatment_creation': - # check_type_field('basetreatment-treatment_creation', - # 'treatment_type', models.TreatmentType, - # 'upstream_is_many') - }, -""" - treatment_creation_wizard = TreatmentWizard.as_view( treatment_wizard_steps, label=_(u"New treatment"), diff --git a/archaeological_finds/wizards.py b/archaeological_finds/wizards.py index 6d4fd2d95..31881fe74 100644 --- a/archaeological_finds/wizards.py +++ b/archaeological_finds/wizards.py @@ -501,3 +501,8 @@ class FindBasketWizard(Wizard): class FindBasketEditWizard(FindBasketWizard): edit = True alt_is_own_method = 'get_write_query_owns' + + +class FindBasketDeletionWizard(DeletionWizard): + wizard_confirm = 'ishtar/wizard/wizard_findbasket_deletion.html' + model = models.FindBasket diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 4cc0ca024..e5246d9bb 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -1176,7 +1176,7 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): for rel in models.Document.RELATED_MODELS: if cleaned_data.get(rel, None): return cleaned_data - raise forms.ValidationError(_(u"A document have to attached at least " + raise forms.ValidationError(_(u"A document has to be attached at least " u"to one item")) def save(self, commit=True): diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 1a0d80ac3..219b6b266 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -3432,7 +3432,7 @@ class IshtarUser(FullSearch): return self.person.full_label() -class Basket(FullSearch): +class Basket(FullSearch, OwnPerms): """ Abstract class for a basket Subclass must be defined with an "items" ManyToManyField @@ -3486,6 +3486,35 @@ class Basket(FullSearch): return "{}-{}".format(datetime.date.today().strftime( "%Y-%m-%d"), slugify(self.label)) + @classmethod + def get_query_owns(cls, ishtaruser): + return Q(user=ishtaruser) | Q(shared_with=ishtaruser) | Q( + shared_write_with=ishtaruser) + + @classmethod + def get_write_query_owns(cls, ishtaruser): + return Q(user=ishtaruser) + + def duplicate(self, ishtaruser=None): + """ + Duplicate the basket. Items in basket are copied but not shared users + :param ishtaruser: if provided an alternate user is used + :return: the new basket + """ + items = list(self.items.all()) + new_item = self + new_item.pk = None + if ishtaruser: + new_item.user = ishtaruser + label = new_item.label + while self.__class__.objects.filter( + label=label, user=new_item.user).count(): + label += unicode(_(u" - duplicate")) + new_item.save() + for item in items: + new_item.add(item) + return new_item + class AuthorType(GeneralType): order = models.IntegerField(_(u"Order"), default=1) diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index f34b2357f..2d8b6b828 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -65,17 +65,29 @@ CURRENT_ITEM_KEYS = ( CURRENT_ITEM_KEYS_DICT = dict(CURRENT_ITEM_KEYS) +def get_autocomplete_query(request, label_attributes, extra=None): + q = request.GET.get('term') or "" + if not label_attributes: + return Q(pk__isnull=True) + query = Q() + if extra: + query = Q(**extra) + for q in q.split(' '): + if not q: + continue + sub_q = Q(**{label_attributes[0] + "__icontains": q}) + for other_label in label_attributes[1:]: + sub_q = sub_q | Q(**{other_label + "__icontains": q}) + query = query & sub_q + return query + + def get_autocomplete_item(model, extra=None): if not extra: extra = {} def func(request, current_right=None): - q = request.GET.get('term') or "" - query = Q(**extra) - for q in q.split(' '): - if not q: - continue - query = query & Q(cached_label__icontains=q) + query = get_autocomplete_query(request, ['cached_label'], extra=extra) limit = 20 objects = model.objects.filter(query)[:limit] data = json.dumps([{'id': obj.pk, 'value': obj.cached_label} -- cgit v1.2.3 From 8c6bd7f82712f849fb641e71cf95d5a05a26d198 Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Fri, 30 Nov 2018 19:59:16 +0100 Subject: Basket: duplicate QA --- archaeological_finds/forms.py | 31 +++++++++++++++++++++- archaeological_finds/models_finds.py | 17 +++++++++++- .../ishtar/forms/qa_findbasket_duplicate.html | 22 +++++++++++++++ .../ishtar/wizard/wizard_findbasket_deletion.html | 7 +++++ archaeological_finds/urls.py | 5 ++++ archaeological_finds/views.py | 26 ++++++++++++++++++ ishtar_common/models.py | 9 ++++--- ishtar_common/templates/ishtar/forms/qa_base.html | 3 +-- 8 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 archaeological_finds/templates/ishtar/forms/qa_findbasket_duplicate.html (limited to 'ishtar_common/models.py') diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py index f78b4a5f3..e896b310d 100644 --- a/archaeological_finds/forms.py +++ b/archaeological_finds/forms.py @@ -84,7 +84,7 @@ __all__ = [ 'FindDeletionForm', 'UpstreamFindFormSelection', 'NewFindBasketForm', 'SelectFindBasketForm', 'FindBasketAddItemForm', 'QAFindFormSingle', 'QAFindFormMulti', 'QAFindBasketForm', - 'QAFindTreatmentForm', + 'QAFindTreatmentForm', 'QAFindbasketDuplicateForm', 'N1TreatmentForm', 'OneNTreatmentForm', 'ResultingFindForm', 'ResultingFindsForm', 'SingleUpstreamFindFormSelection' ] @@ -695,6 +695,35 @@ class QAFindBasketForm(IshtarForm): basket.items.add(item) +class QAFindbasketDuplicateForm(IshtarForm): + label = forms.CharField(label="", max_length=None, required=True) + + 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.basket = kwargs.pop('items')[0] + super(QAFindbasketDuplicateForm, self).__init__(*args, **kwargs) + self.fields['label'].initial = self.basket.label + unicode( + _(u" - duplicate")) + + def clean(self): + label = self.cleaned_data['label'].strip() + if not label: + raise forms.ValidationError(_(u"A label is required.")) + if models.FindBasket.objects.filter(user=self.user, + label=label).count(): + raise forms.ValidationError(_(u"A basket with this label already " + u"exists.")) + return self.cleaned_data + + def save(self): + self.basket.duplicate(label=self.cleaned_data['label'], + ishtaruser=self.user) + + class PreservationForm(CustomForm, ManageOldType): form_label = _("Preservation") form_admin_name = _(u"Find - 030 - Preservation") diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 75ea69ac1..b1dfdba29 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -580,11 +580,19 @@ WEIGHT_UNIT = (('g', _(u"g")), ('kg', _(u"kg")),) -class FindBasket(Basket): +class FindBasket(Basket, MainItem): SHOW_URL = 'show-findbasket' items = models.ManyToManyField('Find', blank=True, related_name='basket') + QUICK_ACTIONS = [ + QuickAction( + url="findbasket-qa-duplicate", icon_class="fa fa-clone", + text=_(u"Duplicate"), target="one", + rights=['view_find', 'view_own_find']), + ] + class Meta: + verbose_name = _(u"Basket") permissions = ( ("view_find", u"Can view all Finds"), ("view_own_find", u"Can view own Find"), @@ -608,6 +616,13 @@ class FindBasket(Basket): _(u"Manage basket"), "fa fa-shopping-basket", "", "", False), ] + + duplicate = self.QUICK_ACTIONS[0] + actions += [ + (reverse(duplicate.url, args=[self.pk]), + duplicate.text, duplicate.icon_class, + "", "", True), + ] return actions diff --git a/archaeological_finds/templates/ishtar/forms/qa_findbasket_duplicate.html b/archaeological_finds/templates/ishtar/forms/qa_findbasket_duplicate.html new file mode 100644 index 000000000..b9ec50f22 --- /dev/null +++ b/archaeological_finds/templates/ishtar/forms/qa_findbasket_duplicate.html @@ -0,0 +1,22 @@ +{% extends "ishtar/forms/qa_base.html" %} +{% load i18n inline_formset table_form %} + +{% block main_form %} +
+ {% trans "Items of the basket will be attached to the new basket but not the shares." %} +
+{% if form.non_field_errors %} + +{% endif %} +
+
+ +
+ {% with form.label as field %} + {% include "blocks/bs_field_snippet.html" %} + {% endwith %} +
+{% endblock %} + diff --git a/archaeological_finds/templates/ishtar/wizard/wizard_findbasket_deletion.html b/archaeological_finds/templates/ishtar/wizard/wizard_findbasket_deletion.html index 02f7253d7..ffd5f0398 100644 --- a/archaeological_finds/templates/ishtar/wizard/wizard_findbasket_deletion.html +++ b/archaeological_finds/templates/ishtar/wizard/wizard_findbasket_deletion.html @@ -12,6 +12,13 @@ {% trans "Are you sure you want to delete this basket?" %}
{% endif %} +
+ {% trans "Items inside the basket (these items will not be deleted):" %} +
+ +
{% trans "Basket informations:" %}
diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py index 9f8776e62..c5a0f7aa2 100644 --- a/archaeological_finds/urls.py +++ b/archaeological_finds/urls.py @@ -84,6 +84,11 @@ urlpatterns = [ check_rights(['view_find', 'view_own_find'])( views.basket_delete_wizard), name='find_basket_deletion'), + url(r'^findbasket-qa-duplicate/(?P[0-9-]+)?/$', + check_rights(['view_find', 'view_own_find'])( + views.QAFindbasketDuplicateFormView.as_view()), + name='findbasket-qa-duplicate'), + url(r'^find-qa-bulk-update/(?P[0-9-]+)?/$', check_rights(['change_find', 'change_own_find'])( views.QAFindForm.as_view()), diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index 28c9495a3..81b032d0b 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -727,3 +727,29 @@ class QAFindTreatmentFormView(QAItemForm): def form_valid(self, form): form.save(self.items, self.request.user) return HttpResponseRedirect(reverse("success")) + + +class QAFindbasketDuplicateFormView(QAItemForm): + template_name = 'ishtar/forms/qa_findbasket_duplicate.html' + model = models.FindBasket + page_name = _(u"Duplicate") + modal_size = "small" + form_class = QAFindbasketDuplicateForm + + def get_quick_action(self): + return models.FindBasket.QUICK_ACTIONS[0] + + def get_form_kwargs(self): + kwargs = super(QAFindbasketDuplicateFormView, 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(QAFindbasketDuplicateFormView, self).get_context_data( + **kwargs) + data['action_name'] = _(u"Duplicate") + return data diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 219b6b266..b6c8fbb3d 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -3495,9 +3495,10 @@ class Basket(FullSearch, OwnPerms): def get_write_query_owns(cls, ishtaruser): return Q(user=ishtaruser) - def duplicate(self, ishtaruser=None): + def duplicate(self, label=None, ishtaruser=None): """ Duplicate the basket. Items in basket are copied but not shared users + :param label: if provided use the name :param ishtaruser: if provided an alternate user is used :return: the new basket """ @@ -3506,13 +3507,15 @@ class Basket(FullSearch, OwnPerms): new_item.pk = None if ishtaruser: new_item.user = ishtaruser - label = new_item.label + if not label: + label = new_item.label while self.__class__.objects.filter( label=label, user=new_item.user).count(): label += unicode(_(u" - duplicate")) + new_item.label = label new_item.save() for item in items: - new_item.add(item) + new_item.items.add(item) return new_item diff --git a/ishtar_common/templates/ishtar/forms/qa_base.html b/ishtar_common/templates/ishtar/forms/qa_base.html index 70fe70e65..367acfcd8 100644 --- a/ishtar_common/templates/ishtar/forms/qa_base.html +++ b/ishtar_common/templates/ishtar/forms/qa_base.html @@ -1,7 +1,6 @@ {% load i18n inline_formset table_form %} -