diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2018-11-30 18:47:14 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2018-11-30 18:48:21 +0100 |
commit | f5f97fb1d89a4ccfc0219c17aa3e8bac6f7fd357 (patch) | |
tree | e091e17882ca67be8e6040e938439fa5a5e6bc5a | |
parent | 0493613840e21a9b72ea6d9d5eaa90968dc7f472 (diff) | |
download | Ishtar-f5f97fb1d89a4ccfc0219c17aa3e8bac6f7fd357.tar.bz2 Ishtar-f5f97fb1d89a4ccfc0219c17aa3e8bac6f7fd357.zip |
Manage basket to treatment file association
-rw-r--r-- | archaeological_finds/forms.py | 21 | ||||
-rw-r--r-- | archaeological_finds/forms_treatments.py | 12 | ||||
-rw-r--r-- | archaeological_finds/migrations/0043_auto_20181130_1310.py | 34 | ||||
-rw-r--r-- | archaeological_finds/models_finds.py | 12 | ||||
-rw-r--r-- | archaeological_finds/models_treatments.py | 6 | ||||
-rw-r--r-- | archaeological_finds/templates/ishtar/sheet_treatmentfile.html | 1 | ||||
-rw-r--r-- | archaeological_finds/templates/ishtar/wizard/wizard_findbasket_deletion.html | 18 | ||||
-rw-r--r-- | archaeological_finds/templates/ishtar/wizard/wizard_treatement_deletion.html | 4 | ||||
-rw-r--r-- | archaeological_finds/tests.py | 3 | ||||
-rw-r--r-- | archaeological_finds/urls.py | 10 | ||||
-rw-r--r-- | archaeological_finds/views.py | 67 | ||||
-rw-r--r-- | archaeological_finds/wizards.py | 5 | ||||
-rw-r--r-- | ishtar_common/forms_common.py | 2 | ||||
-rw-r--r-- | ishtar_common/models.py | 31 | ||||
-rw-r--r-- | ishtar_common/views_item.py | 24 |
15 files changed, 164 insertions, 86 deletions
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 @@ <div class="row"> {% 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 %} +<div class="alert alert-{% if has_downstream %}danger{% else %}warning{% endif%}"> + <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> + {% trans "This basket is attached to treatments requests:" %} + <ul>{% for tf in current_object.treatment_files.all %} + <li>{{ tf }} {{tf|link_to_window}}</li> + {% endfor %}</ul> + {% trans "Are you sure you want to delete this basket?" %} +</div> +{% endif %} +<div class="alert alert-info"> + {% trans "Basket informations:" %} +</div> +{% 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."%} <ul>{% for item in current_object.downstream.all %} <li> - {{item}} + {{item}} {{item|link_to_window}} </li> {% endfor %}</ul> {% 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<step>.+)?$', 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<pks>[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} |