summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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
commiteddd946150bcd9d4ddb05000806cc2cdfb9af60d (patch)
treee091e17882ca67be8e6040e938439fa5a5e6bc5a
parentafd7fb9c2be01a44a45f582eebdf02632a10be99 (diff)
downloadIshtar-eddd946150bcd9d4ddb05000806cc2cdfb9af60d.tar.bz2
Ishtar-eddd946150bcd9d4ddb05000806cc2cdfb9af60d.zip
Manage basket to treatment file association
-rw-r--r--archaeological_finds/forms.py21
-rw-r--r--archaeological_finds/forms_treatments.py12
-rw-r--r--archaeological_finds/migrations/0043_auto_20181130_1310.py34
-rw-r--r--archaeological_finds/models_finds.py12
-rw-r--r--archaeological_finds/models_treatments.py6
-rw-r--r--archaeological_finds/templates/ishtar/sheet_treatmentfile.html1
-rw-r--r--archaeological_finds/templates/ishtar/wizard/wizard_findbasket_deletion.html18
-rw-r--r--archaeological_finds/templates/ishtar/wizard/wizard_treatement_deletion.html4
-rw-r--r--archaeological_finds/tests.py3
-rw-r--r--archaeological_finds/urls.py10
-rw-r--r--archaeological_finds/views.py67
-rw-r--r--archaeological_finds/wizards.py5
-rw-r--r--ishtar_common/forms_common.py2
-rw-r--r--ishtar_common/models.py31
-rw-r--r--ishtar_common/views_item.py24
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}