diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2020-04-08 18:06:26 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-02-28 12:15:20 +0100 |
commit | 9be03de7917bb321119641b9d0063cd060d9b3c1 (patch) | |
tree | 47c2df9adb1820c3993f1e50743dafcf86262e15 | |
parent | b2bb294b82a968b899a852f4fa64495e9cc2984f (diff) | |
download | Ishtar-9be03de7917bb321119641b9d0063cd060d9b3c1.tar.bz2 Ishtar-9be03de7917bb321119641b9d0063cd060d9b3c1.zip |
New container management - merge action
-rw-r--r-- | archaeological_warehouse/forms.py | 170 | ||||
-rw-r--r-- | archaeological_warehouse/ishtar_menu.py | 8 | ||||
-rw-r--r-- | archaeological_warehouse/migrations/0107_auto_20200407_1553.py | 35 | ||||
-rw-r--r-- | archaeological_warehouse/models.py | 104 | ||||
-rw-r--r-- | archaeological_warehouse/templates/ishtar/merge_container.html | 23 | ||||
-rw-r--r-- | archaeological_warehouse/tests.py | 61 | ||||
-rw-r--r-- | archaeological_warehouse/urls.py | 9 | ||||
-rw-r--r-- | archaeological_warehouse/views.py | 97 | ||||
-rw-r--r-- | ishtar_common/forms_common.py | 355 | ||||
-rw-r--r-- | ishtar_common/models.py | 4 | ||||
-rw-r--r-- | ishtar_common/views.py | 7 |
11 files changed, 532 insertions, 341 deletions
diff --git a/archaeological_warehouse/forms.py b/archaeological_warehouse/forms.py index a7390a890..83289819e 100644 --- a/archaeological_warehouse/forms.py +++ b/archaeological_warehouse/forms.py @@ -44,12 +44,13 @@ from ishtar_common.forms import name_validator, reverse_lazy, \ get_form_selection, ManageOldType, FinalForm, FormSet, \ CustomForm, FieldType, DocumentItemSelect, FormHeader, TableSelect, \ CustomFormSearch, MultiSearchForm, LockForm -from ishtar_common.forms_common import get_town_field +from ishtar_common.forms_common import get_town_field, MergeForm, ManualMerge,\ + MergeIntoForm from archaeological_finds.forms import FindMultipleFormSelection, \ SelectFindBasketForm -def get_warehouse_field(label=_(u"Warehouse"), required=True): +def get_warehouse_field(label=_("Warehouse"), required=True): # !FIXME hard_link, reverse_lazy doen't seem to work with formsets url = "/" + settings.URL_PATH + 'autocomplete-warehouse' widget = widgets.JQueryAutoComplete(url, associated_model=models.Warehouse) @@ -58,14 +59,14 @@ def get_warehouse_field(label=_(u"Warehouse"), required=True): class SelectedDivisionForm(ManageOldType, forms.Form): - form_label = _(u"Division") + form_label = _("Division") base_model = 'associated_division' associated_models = {'division': models.WarehouseDivision, 'associated_division': models.WarehouseDivisionLink} division = forms.ChoiceField( - label=_(u"Division"), choices=(), + label=_("Division"), choices=(), validators=[valid_id(models.WarehouseDivision)]) - order = forms.IntegerField(label=_(u"Order"), min_value=0, required=False) + order = forms.IntegerField(label=_("Order"), min_value=0, required=False) def __init__(self, *args, **kwargs): super(SelectedDivisionForm, self).__init__(*args, **kwargs) @@ -79,24 +80,24 @@ class DivisionFormSet(FormSet): def clean(self): """Checks that no divisions are duplicated.""" self.check_duplicate(('division',), _("There are identical divisions.")) - self.check_duplicate(('order',), _(u"Order fields must be different."), + self.check_duplicate(('order',), _("Order fields must be different."), check_null=True) SelectedDivisionFormset = formset_factory( SelectedDivisionForm, can_delete=True, formset=DivisionFormSet) -SelectedDivisionFormset.form_label = _(u"Divisions") -SelectedDivisionFormset.form_admin_name = _(u"Warehouse - 020 - Divisions") +SelectedDivisionFormset.form_label = _("Divisions") +SelectedDivisionFormset.form_admin_name = _("Warehouse - 020 - Divisions") SelectedDivisionFormset.form_slug = "warehouse-020-divisions" class WarehouseSelect(CustomForm, TableSelect): _model = models.Warehouse - form_admin_name = _(u"Warehouse - 001 - Search") + form_admin_name = _("Warehouse - 001 - Search") form_slug = "warehouse-001-search" search_vector = forms.CharField( - label=_(u"Full text search"), widget=widgets.SearchWidget( + label=_("Full text search"), widget=widgets.SearchWidget( 'archaeological-warehouse', 'warehouse' )) name = forms.CharField(label=_("Name")) @@ -139,8 +140,8 @@ class WarehouseFormMultiSelection(LockForm, MultiSearchForm): class WarehouseForm(CustomForm, ManageOldType, forms.Form): HEADERS = {} - form_label = _(u"Warehouse") - form_admin_name = _(u"Warehouse - 010 - General") + form_label = _("Warehouse") + form_admin_name = _("Warehouse - 010 - General") form_slug = "warehouse-010-general" extra_form_modals = ["organization", "person"] associated_models = { @@ -151,9 +152,9 @@ class WarehouseForm(CustomForm, ManageOldType, forms.Form): 'spatial_reference_system': SpatialReferenceSystem } - name = forms.CharField(label=_(u"Name"), max_length=200, + name = forms.CharField(label=_("Name"), max_length=200, validators=[name_validator]) - warehouse_type = forms.ChoiceField(label=_(u"Warehouse type"), + warehouse_type = forms.ChoiceField(label=_("Warehouse type"), choices=[]) organization = forms.IntegerField( label=_("Organization"), @@ -173,28 +174,28 @@ class WarehouseForm(CustomForm, ManageOldType, forms.Form): label=_("Create a new organization from this warehouse"), required=False ) - comment = forms.CharField(label=_(u"Comment"), widget=forms.Textarea, + comment = forms.CharField(label=_("Comment"), widget=forms.Textarea, required=False) HEADERS['address'] = FormHeader( - _(u"Address"), collapse=True, + _("Address"), collapse=True, help_message=_( "Only fill the following fields if no organization is provided or " "if the address of the warehouse is different from the one of the " "organization. If a new organization is created from this " "warehouse, the following fields are used for the organization.")) - address = forms.CharField(label=_(u"Address"), widget=forms.Textarea, + address = forms.CharField(label=_("Address"), widget=forms.Textarea, required=False) - address_complement = forms.CharField(label=_(u"Address complement"), + address_complement = forms.CharField(label=_("Address complement"), widget=forms.Textarea, required=False) - postal_code = forms.CharField(label=_(u"Postal code"), max_length=10, + postal_code = forms.CharField(label=_("Postal code"), max_length=10, required=False) - town = forms.CharField(label=_(u"Town (freeform)"), max_length=150, + town = forms.CharField(label=_("Town (freeform)"), max_length=150, required=False) precise_town = get_town_field(required=False) - country = forms.CharField(label=_(u"Country"), max_length=30, + country = forms.CharField(label=_("Country"), max_length=30, required=False) - phone = forms.CharField(label=_(u"Phone"), max_length=18, required=False) - mobile_phone = forms.CharField(label=_(u"Mobile phone"), max_length=18, + phone = forms.CharField(label=_("Phone"), max_length=18, required=False) + mobile_phone = forms.CharField(label=_("Mobile phone"), max_length=18, required=False) HEADERS['x'] = FormHeader(_("Coordinates")) x = forms.FloatField(label=_("X"), required=False) @@ -250,13 +251,13 @@ class WarehouseModifyForm(WarehouseForm): class WarehouseDeletionForm(FinalForm): - confirm_msg = _(u"Would you like to delete this warehouse?") - confirm_end_msg = _(u"Would you like to delete this warehouse?") + confirm_msg = _("Would you like to delete this warehouse?") + confirm_end_msg = _("Would you like to delete this warehouse?") class ContainerForm(CustomForm, ManageOldType, forms.Form): - form_label = _(u"Container") - form_admin_name = _(u"Container - 010 - General") + form_label = _("Container") + form_admin_name = _("Container - 010 - General") form_slug = "container-010-general" file_upload = True extra_form_modals = ["warehouse", "organization", "person", "container"] @@ -264,10 +265,10 @@ class ContainerForm(CustomForm, ManageOldType, forms.Form): 'location': models.Warehouse, 'parent': models.Container, 'responsible': models.Warehouse} - reference = forms.CharField(label=_(u"Ref."), max_length=200) - old_reference = forms.CharField(label=_(u"Old reference"), required=False, + reference = forms.CharField(label=_("Ref."), max_length=200) + old_reference = forms.CharField(label=_("Old reference"), required=False, max_length=200) - container_type = forms.ChoiceField(label=_(u"Container type"), choices=[]) + container_type = forms.ChoiceField(label=_("Container type"), choices=[]) parent = forms.IntegerField( label=_("Parent container"), widget=widgets.JQueryAutoComplete( @@ -277,18 +278,18 @@ class ContainerForm(CustomForm, ManageOldType, forms.Form): required=False ) responsible = forms.IntegerField( - label=_(u"Responsible warehouse"), + label=_("Responsible warehouse"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-warehouse'), associated_model=models.Warehouse, new=True), validators=[valid_id(models.Warehouse)]) location = forms.IntegerField( - label=_(u"Current location (warehouse)"), + label=_("Current location (warehouse)"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-warehouse'), associated_model=models.Warehouse, new=True), validators=[valid_id(models.Warehouse)]) - comment = forms.CharField(label=_(u"Comment"), + comment = forms.CharField(label=_("Comment"), widget=forms.Textarea, required=False) TYPES = [ FieldType('container_type', models.ContainerType), @@ -306,12 +307,15 @@ class ContainerForm(CustomForm, ManageOldType, forms.Form): cleaned_data = self.cleaned_data warehouse = cleaned_data.get("location") q = models.Container.objects.filter( - reference=cleaned_data.get("reference"), location__pk=warehouse) + reference=cleaned_data.get("reference"), location__pk=warehouse, + container_type_id=cleaned_data.get("container_type"), + parent_id=cleaned_data.get("parent") + ) if 'pk' in cleaned_data and cleaned_data['pk']: q = q.exclude(pk=int(cleaned_data['pk'])) if q.count(): - raise forms.ValidationError(_(u"This reference already exists for " - u"this warehouse.")) + raise forms.ValidationError(_("This reference already exists for " + "this warehouse.")) return cleaned_data def save(self, user): @@ -328,7 +332,7 @@ class ContainerForm(CustomForm, ManageOldType, forms.Form): class ContainerModifyForm(ContainerForm): pk = forms.IntegerField(required=False, widget=forms.HiddenInput) - index = forms.IntegerField(label=_(u"ID"), required=False) + index = forms.IntegerField(label=_("ID"), required=False) def __init__(self, *args, **kwargs): super(ContainerModifyForm, self).__init__(*args, **kwargs) @@ -358,25 +362,25 @@ class ContainerModifyForm(ContainerForm): if 'pk' in cleaned_data and cleaned_data['pk']: q = q.exclude(pk=int(cleaned_data['pk'])) if q.count(): - raise forms.ValidationError(_(u"This ID already exists for " - u"this warehouse.")) + raise forms.ValidationError(_("This ID already exists for " + "this warehouse.")) return cleaned_data class ContainerSelect(DocumentItemSelect): _model = models.Container - form_admin_name = _(u"Container - 001 - Search") + form_admin_name = _("Container - 001 - Search") form_slug = "container-001-search" search_vector = forms.CharField( - label=_(u"Full text search"), widget=widgets.SearchWidget( + label=_("Full text search"), widget=widgets.SearchWidget( 'archaeological-warehouse', 'container' )) location_name = get_warehouse_field(label=_("Warehouse")) container_type = forms.ChoiceField(label=_("Container type"), choices=[]) reference = forms.CharField(label=_("Ref.")) old_reference = forms.CharField(label=_("Old reference")) - comment = forms.CharField(label=_(u"Comment")) + comment = forms.CharField(label=_("Comment")) contain_containers = forms.NullBooleanField(label=_("Contain containers")) empty = forms.NullBooleanField(label=_("Currently empty")) is_stationary = forms.NullBooleanField(label=_("Is stationary")) @@ -388,7 +392,7 @@ class ContainerSelect(DocumentItemSelect): associated_model=ArchaeologicalSite), validators=[valid_id(ArchaeologicalSite)]) archaeological_sites_name = forms.CharField( - label=_(u"Archaeological site name (attached to the operation)") + label=_("Archaeological site name (attached to the operation)") ) archaeological_sites_context_record = forms.IntegerField( label=_("Archaeological site (attached to the context record)"), @@ -397,7 +401,7 @@ class ContainerSelect(DocumentItemSelect): associated_model=ArchaeologicalSite), validators=[valid_id(ArchaeologicalSite)]) archaeological_sites_context_record_name = forms.CharField( - label=_(u"Archaeological site name (attached to the context record)") + label=_("Archaeological site name (attached to the context record)") ) code_patriarche = forms.IntegerField(label=_("Operation - Code PATRIARCHE"), widget=OAWidget) @@ -415,33 +419,33 @@ class ContainerSelect(DocumentItemSelect): validators=[valid_id(ContextRecord)]) find_label = forms.CharField(label=_("Find - Label")) find_denomination = forms.CharField(label=_("Find - Denomination")) - description = forms.CharField(label=_(u"Find - Description")) + description = forms.CharField(label=_("Find - Description")) material_types = forms.IntegerField( - label=_(u"Material type"), + label=_("Material type"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-materialtype'), associated_model=MaterialType), ) object_types = forms.IntegerField( - label=_(u"Object type"), + label=_("Object type"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-objecttype'), associated_model=ObjectType), ) - integrities = forms.ChoiceField(label=_(u"Integrity / interest"), + integrities = forms.ChoiceField(label=_("Integrity / interest"), choices=[]) - remarkabilities = forms.ChoiceField(label=_(u"Remarkability"), + remarkabilities = forms.ChoiceField(label=_("Remarkability"), choices=[]) - conservatory_state = forms.ChoiceField(label=_(u"Conservatory state"), + conservatory_state = forms.ChoiceField(label=_("Conservatory state"), choices=[]) alterations = forms.ChoiceField( - label=_(u"Alteration"), choices=[]) + label=_("Alteration"), choices=[]) alteration_causes = forms.ChoiceField( - label=_(u"Alteration cause"), choices=[]) + label=_("Alteration cause"), choices=[]) preservation_to_considers = forms.ChoiceField( - choices=[], label=_(u"Preservation type")) + choices=[], label=_("Preservation type")) treatment_emergency = forms.ChoiceField( - choices=[], label=_(u"Treatment emergency") + choices=[], label=_("Treatment emergency") ) TYPES = [ @@ -463,56 +467,82 @@ class ContainerSelect(DocumentItemSelect): ContainerFormSelection = get_form_selection( - 'ContainerFormSelection', _(u"Container search"), 'container', + 'ContainerFormSelection', _("Container search"), 'container', models.Container, ContainerSelect, 'get-container', - _(u"You should select a container."), new=True, - new_message=_(u"Add a new container"), + _("You should select a container."), new=True, + new_message=_("Add a new container"), base_form_select=(LockForm, CustomFormSearch) ) MainContainerFormSelection = get_form_selection( - 'ContainerFormSelection', _(u"Container search"), 'pk', + 'ContainerFormSelection', _("Container search"), 'pk', models.Container, ContainerSelect, 'get-container', - _(u"You should select a container."), gallery=True, map=True, + _("You should select a container."), gallery=True, map=True, base_form_select=CustomFormSearch ) MainContainerFormMultiSelection = get_form_selection( - 'ContainerFormSelection', _(u"Container search"), 'pks', + 'ContainerFormSelection', _("Container search"), 'pks', models.Container, ContainerSelect, 'get-container', - _(u"You should select a container."), gallery=True, map=True, + _("You should select a container."), gallery=True, map=True, alt_pk_field="pk", multi=True, base_form_select=(LockForm, MultiSearchForm) ) +class MergeContainerForm(MergeForm): + class Meta: + model = models.Container + fields = [] + + FROM_KEY = 'from_container' + TO_KEY = 'to_container' + + +class ContainerMergeFormSelection(ManualMerge, forms.Form): + SEARCH_AND_SELECT = True + form_label = _("Container to merge") + associated_models = {'to_merge': models.Container} + currents = {'to_merge': models.Container} + to_merge = forms.CharField( + label="", required=False, + widget=widgets.DataTable( + reverse_lazy('get-container'), ContainerSelect, + models.Container, + multiple_select=True,),) + + +class ContainerMergeIntoForm(MergeIntoForm): + associated_model = models.Container + + class BasePackagingForm(SelectFindBasketForm): - form_label = _(u"Packaging") + form_label = _("Packaging") associated_models = {'treatment_type': TreatmentType, 'person': Person, 'location': models.Warehouse, 'basket': FindBasket} person = forms.IntegerField( - label=_(u"Packager"), + label=_("Packager"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-person'), associated_model=Person, new=True), validators=[valid_id(Person)]) start_date = forms.DateField( - label=_(u"Date"), required=False, widget=DatePicker, + label=_("Date"), required=False, widget=DatePicker, initial=datetime.date.today ) class FindPackagingFormSelection(FindMultipleFormSelection): - form_label = _(u"Packaged finds") + form_label = _("Packaged finds") class LocalisationForm(CustomForm, forms.Form): - form_admin_name = _(u"Container - 020 - Localisation") + form_admin_name = _("Container - 020 - Localisation") form_slug = "container-020-localisation" - form_label = _(u"Localisation") + form_label = _("Localisation") def __init__(self, *args, **kwargs): self.container, self.warehouse = None, None @@ -524,7 +554,7 @@ class LocalisationForm(CustomForm, forms.Form): if not self.warehouse: return for divlink in self.warehouse.divisions.order_by('order').all(): - initial = u"-" + initial = "-" if self.container: q = models.ContainerLocalisation.objects.filter( division__division=divlink.division, @@ -537,5 +567,5 @@ class LocalisationForm(CustomForm, forms.Form): class ContainerDeletionForm(FinalForm): - confirm_msg = _(u"Would you like to delete this container?") - confirm_end_msg = _(u"Would you like to delete this container?") + confirm_msg = _("Would you like to delete this container?") + confirm_end_msg = _("Would you like to delete this container?") diff --git a/archaeological_warehouse/ishtar_menu.py b/archaeological_warehouse/ishtar_menu.py index b7c820d13..17acae47a 100644 --- a/archaeological_warehouse/ishtar_menu.py +++ b/archaeological_warehouse/ishtar_menu.py @@ -58,6 +58,14 @@ MENU_SECTIONS = [ model=models.Warehouse, access_controls=['change_container', 'change_own_container']), + MenuItem( + 'container-merge', _(u"Automatic merge"), + model=models.Container, + access_controls=['administrator']), + MenuItem( + 'container-manual-merge', _(u"Manual merge"), + model=models.Container, + access_controls=['administrator']), MenuItem('container_deletion', _(u"Deletion"), model=models.Warehouse, access_controls=['change_container', diff --git a/archaeological_warehouse/migrations/0107_auto_20200407_1553.py b/archaeological_warehouse/migrations/0107_auto_20200407_1553.py new file mode 100644 index 000000000..56aeddd47 --- /dev/null +++ b/archaeological_warehouse/migrations/0107_auto_20200407_1553.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.27 on 2020-04-07 15:53 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_warehouse', '0106_auto_20200407_1414'), + ] + + operations = [ + migrations.AddField( + model_name='container', + name='archived', + field=models.NullBooleanField(default=False), + ), + migrations.AddField( + model_name='container', + name='merge_candidate', + field=models.ManyToManyField(blank=True, related_name='_container_merge_candidate_+', to='archaeological_warehouse.Container'), + ), + migrations.AddField( + model_name='container', + name='merge_exclusion', + field=models.ManyToManyField(blank=True, related_name='_container_merge_exclusion_+', to='archaeological_warehouse.Container'), + ), + migrations.AddField( + model_name='container', + name='merge_key', + field=models.TextField(blank=True, null=True, verbose_name='Merge key'), + ), + ] diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py index e62575ccf..53e50976d 100644 --- a/archaeological_warehouse/models.py +++ b/archaeological_warehouse/models.py @@ -35,7 +35,8 @@ from ishtar_common.models import Document, GeneralType, get_external_id, \ LightHistorizedItem, OwnPerms, Address, Person, post_save_cache, \ DashboardFormItem, ShortMenuItem, Organization, OrganizationType, \ document_attached_changed, SearchAltName, DynamicRequest, GeoItem, \ - QRCodeItem, SearchVectorConfig, DocumentItem, QuickAction, MainItem + QRCodeItem, SearchVectorConfig, DocumentItem, QuickAction, MainItem, \ + Merge from ishtar_common.model_merging import merge_model_objects from ishtar_common.utils import cached_label_changed, \ cached_label_and_geo_changed @@ -43,8 +44,8 @@ from ishtar_common.utils import cached_label_changed, \ class WarehouseType(GeneralType): class Meta: - verbose_name = _(u"Warehouse type") - verbose_name_plural = _(u"Warehouse types") + verbose_name = _("Warehouse type") + verbose_name_plural = _("Warehouse types") ordering = ('label',) @@ -97,7 +98,7 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, QA_LOCK = QuickAction( url="warehouse-qa-lock", icon_class="fa fa-lock", - text=_(u"Lock/Unlock"), target="many", + text=_("Lock/Unlock"), target="many", rights=['change_warehouse', 'change_own_warehouse'] ) QUICK_ACTIONS = [QA_LOCK] @@ -105,41 +106,41 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, objects = UUIDModelManager() uuid = models.UUIDField(default=uuid.uuid4) - name = models.CharField(_(u"Name"), max_length=200) + name = models.CharField(_("Name"), max_length=200) warehouse_type = models.ForeignKey(WarehouseType, - verbose_name=_(u"Warehouse type")) + verbose_name=_("Warehouse type")) person_in_charge = models.ForeignKey( Person, on_delete=models.SET_NULL, related_name='warehouse_in_charge', - verbose_name=_(u"Person in charge"), null=True, blank=True) + verbose_name=_("Person in charge"), null=True, blank=True) organization = models.ForeignKey( Organization, blank=True, null=True, related_name='warehouses', verbose_name=_("Organization"), on_delete=models.SET_NULL) - comment = models.TextField(_(u"Comment"), null=True, blank=True) + comment = models.TextField(_("Comment"), null=True, blank=True) associated_divisions = models.ManyToManyField( 'WarehouseDivision', verbose_name=_("Divisions"), blank=True, through='WarehouseDivisionLink' ) documents = models.ManyToManyField( - Document, related_name='warehouses', verbose_name=_(u"Documents"), + Document, related_name='warehouses', verbose_name=_("Documents"), blank=True) main_image = models.ForeignKey( Document, related_name='main_image_warehouses', on_delete=models.SET_NULL, - verbose_name=_(u"Main image"), blank=True, null=True) - external_id = models.TextField(_(u"External ID"), blank=True, null=True) + verbose_name=_("Main image"), blank=True, null=True) + external_id = models.TextField(_("External ID"), blank=True, null=True) auto_external_id = models.BooleanField( - _(u"External ID is set automatically"), default=False) + _("External ID is set automatically"), default=False) SUB_ADDRESSES = ["organization", "person_in_charge"] class Meta: - verbose_name = _(u"Warehouse") - verbose_name_plural = _(u"Warehouses") + verbose_name = _("Warehouse") + verbose_name_plural = _("Warehouses") permissions = ( - ("view_warehouse", u"Can view all Warehouses"), - ("view_own_warehouse", u"Can view own Warehouse"), - ("add_own_warehouse", u"Can add own Warehouse"), - ("change_own_warehouse", u"Can change own Warehouse"), - ("delete_own_warehouse", u"Can delete own Warehouse"), + ("view_warehouse", "Can view all Warehouses"), + ("view_own_warehouse", "Can view own Warehouse"), + ("add_own_warehouse", "Can add own Warehouse"), + ("change_own_warehouse", "Can change own Warehouse"), + ("delete_own_warehouse", "Can delete own Warehouse"), ) indexes = [ GinIndex(fields=['data']), @@ -152,7 +153,7 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, return (self.uuid, ) def _get_base_image_path(self): - return u"{}/{}".format(self.SLUG, self.external_id) + return "{}/{}".format(self.SLUG, self.external_id) def create_attached_organization(self): """ @@ -360,8 +361,8 @@ post_save.connect(cached_label_and_geo_changed, sender=Warehouse) class WarehouseDivision(GeneralType): class Meta: - verbose_name = _(u"Warehouse division type") - verbose_name_plural = _(u"Warehouse division types") + verbose_name = _("Warehouse division type") + verbose_name_plural = _("Warehouse division types") post_save.connect(post_save_cache, sender=WarehouseDivision) @@ -379,16 +380,16 @@ class ContainerType(GeneralType): _("Stationary"), default=False, help_text=_("Container that usually will not be moved. Ex: building, " "room.")) - length = models.IntegerField(_(u"Length (mm)"), blank=True, null=True) - width = models.IntegerField(_(u"Width (mm)"), blank=True, null=True) - height = models.IntegerField(_(u"Height (mm)"), blank=True, null=True) - volume = models.FloatField(_(u"Volume (l)"), blank=True, null=True) - reference = models.CharField(_(u"Ref."), max_length=300, blank=True, + length = models.IntegerField(_("Length (mm)"), blank=True, null=True) + width = models.IntegerField(_("Width (mm)"), blank=True, null=True) + height = models.IntegerField(_("Height (mm)"), blank=True, null=True) + volume = models.FloatField(_("Volume (l)"), blank=True, null=True) + reference = models.CharField(_("Ref."), max_length=300, blank=True, null=True) class Meta: - verbose_name = _(u"Container type") - verbose_name_plural = _(u"Container types") + verbose_name = _("Container type") + verbose_name_plural = _("Container types") ordering = ('label',) @@ -413,7 +414,7 @@ class WarehouseDivisionLink(models.Model): unique_together = ('warehouse', 'division') def __str__(self): - return u"{} - {}".format(self.warehouse, self.division) + return "{} - {}".format(self.warehouse, self.division) def natural_key(self): return self.warehouse.uuid, self.division.txt_idx @@ -458,7 +459,7 @@ class ContainerTree: """ -class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem, +class Container(DocumentItem, Merge, LightHistorizedItem, QRCodeItem, GeoItem, OwnPerms, MainItem): SLUG = 'container' APP = "archaeological-warehouse" @@ -659,7 +660,7 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem, QA_LOCK = QuickAction( url="container-qa-lock", icon_class="fa fa-lock", - text=_(u"Lock/Unlock"), target="many", + text=_("Lock/Unlock"), target="many", rights=['change_container', 'change_own_container'] ) QUICK_ACTIONS = [QA_LOCK] @@ -678,13 +679,13 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem, ) container_type = models.ForeignKey(ContainerType, verbose_name=_("Container type")) - reference = models.TextField(_(u"Container ref.")) - comment = models.TextField(_(u"Comment"), null=True, blank=True) - cached_label = models.TextField(_(u"Localisation"), null=True, blank=True, + reference = models.TextField(_("Container ref.")) + comment = models.TextField(_("Comment"), null=True, blank=True) + cached_label = models.TextField(_("Localisation"), null=True, blank=True, db_index=True) - cached_location = models.TextField(_(u"Cached location"), + cached_location = models.TextField(_("Cached location"), null=True, blank=True, db_index=True) - cached_division = models.TextField(_(u"Cached division"), + cached_division = models.TextField(_("Cached division"), null=True, blank=True, db_index=True) parent = models.ForeignKey("Container", verbose_name=_("Parent container"), on_delete=models.SET_NULL, @@ -712,11 +713,11 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem, ('location', 'container_type', 'parent', 'reference')] permissions = ( - ("view_container", u"Can view all Containers"), - ("view_own_container", u"Can view own Container"), - ("add_own_container", u"Can add own Container"), - ("change_own_container", u"Can change own Container"), - ("delete_own_container", u"Can delete own Container"), + ("view_container", "Can view all Containers"), + ("view_own_container", "Can view own Container"), + ("add_own_container", "Can add own Container"), + ("change_own_container", "Can change own Container"), + ("delete_own_container", "Can delete own Container"), ) indexes = [ GinIndex(fields=['data']), @@ -726,6 +727,10 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem, return self.cached_label or "" @property + def name(self): + return "{} - {}".format(self.container_type.name, self.reference) + + @property def short_label(self): return "{} {}".format(self.container_type.label, self.reference) @@ -767,6 +772,7 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem, return self.location._get_base_image_path() + "/" + self.external_id def merge(self, item, keep_old=False): + # TODO: change localisation management locas = [ cl.division.division.txt_idx for cl in ContainerLocalisation.objects.filter(container=self).all() @@ -1050,7 +1056,7 @@ class Container(DocumentItem, LightHistorizedItem, QRCodeItem, GeoItem, if can_edit_find: actions += [ (reverse('container-add-treatment', args=[self.pk]), - _(u"Add treatment"), "fa fa-flask", "", "", False), + _("Add treatment"), "fa fa-flask", "", "", False), ] return actions @@ -1137,21 +1143,21 @@ class ContainerLocalisationManager(models.Manager): class ContainerLocalisation(models.Model): - container = models.ForeignKey(Container, verbose_name=_(u"Container"), + container = models.ForeignKey(Container, verbose_name=_("Container"), related_name='division') division = models.ForeignKey(WarehouseDivisionLink, - verbose_name=_(u"Division")) - reference = models.CharField(_(u"Reference"), max_length=200, default='') + verbose_name=_("Division")) + reference = models.CharField(_("Reference"), max_length=200, default='') objects = ContainerLocalisationManager() class Meta: - verbose_name = _(u"Container localisation") - verbose_name_plural = _(u"Container localisations") + verbose_name = _("Container localisation") + verbose_name_plural = _("Container localisations") unique_together = ('container', 'division') ordering = ('container', 'division__order') def __str__(self): - lbl = u" - ".join((str(self.container), + lbl = " - ".join((str(self.container), str(self.division), self.reference)) return lbl diff --git a/archaeological_warehouse/templates/ishtar/merge_container.html b/archaeological_warehouse/templates/ishtar/merge_container.html new file mode 100644 index 000000000..558828586 --- /dev/null +++ b/archaeological_warehouse/templates/ishtar/merge_container.html @@ -0,0 +1,23 @@ +{% extends "ishtar/merge.html" %} +{% block merge_field_row %} + {% if form.non_field_errors %}<tr><td colspan='4'></td><td colspan='3' class='errorlist'>{% for error in form.non_field_errors %}{{error}} {% endfor%}</tr>{% endif %} + <tr> + <td> + <a href="#" onclick="load_window('{% url 'show-container' form.instance.from_container.pk '' %}', 'container');" class="display_details"><i class="fa fa-info-circle" aria-hidden="true"></i></a> + </td> + <td> + {{form.instance.from_container}} ({{form.instance.from_container.pk}})<br/> + {{form.instance.from_container.address_lbl|linebreaksbr}} + </td> + <td> + <a href="#" onclick="load_window('{% url 'show-container' form.instance.to_container.pk '' %}', 'container');" class="display_details"><i class="fa fa-info-circle" aria-hidden="true"></i></a> + </td> + <td> + {{form.instance.to_container}} ({{form.instance.to_container.pk}})<br/> + {{form.instance.to_container.address_lbl|linebreaksbr}} + </td> + <td class='check'>{{form.b_is_duplicate_a}}</td> + <td class='check'>{{form.a_is_duplicate_b}}</td> + <td class='check'>{{form.not_duplicate}}</td> + </tr> +{% endblock %} diff --git a/archaeological_warehouse/tests.py b/archaeological_warehouse/tests.py index a342dc1f9..37babf1b2 100644 --- a/archaeological_warehouse/tests.py +++ b/archaeological_warehouse/tests.py @@ -538,6 +538,10 @@ class ContainerTest(FindInit, TestCase): label='division2') self.div_link = models.WarehouseDivisionLink.objects.create( warehouse=self.main_warehouse, division=self.division) + self.alt_warehouse = models.Warehouse.objects.create( + name="Alt", + warehouse_type=models.WarehouseType.objects.all()[0] + ) def test_form_creation(self): data = { @@ -718,3 +722,60 @@ class ContainerTest(FindInit, TestCase): container_2.external_id, ct.txt_idx, container_3.reference)) + def test_merge_candidate(self): + ct = models.ContainerType.objects.all()[0] + container_1 = models.Container.objects.create( + reference="Test", responsible=self.main_warehouse, + location=self.main_warehouse, + container_type=ct) + init_mc = container_1.merge_candidate.count() + models.Container.objects.create( + reference="TEST", responsible=self.main_warehouse, + location=self.main_warehouse, + container_type=ct) + self.assertEqual(container_1.merge_candidate.count(), + init_mc + 1) + container_1.archive() + self.assertEqual(container_1.merge_candidate.count(), + init_mc) + + def test_merge_container(self): + ct = models.ContainerType.objects.all()[0] + ct2 = models.ContainerType.objects.all()[1] + self.create_finds() + self.create_finds() + self.create_finds() + find0 = self.finds[0] + find1 = self.finds[1] + find2 = self.finds[2] + + container_1 = models.Container.objects.create( + reference="Test 1", + location=self.main_warehouse, + container_type=ct) + find0.container = container_1 + find0.container_ref = container_1 + find0.save() + find1.container = container_1 + find1.container_ref = container_1 + find1.save() + + container_2 = models.Container.objects.create( + reference="Test 2", + location=self.alt_warehouse, + container_type=ct2) + find2.container = container_2 + find2.container_ref = container_2 + find2.save() + + container_1.merge(container_2) + container_1 = models.Container.objects.get(pk=container_1.pk) + self.assertEqual( + models.Container.objects.filter(pk=container_2.pk).count(), 0) + + # preserve existing fields + self.assertEqual(container_1.reference, 'Test 1') + self.assertEqual(container_1.container_type, ct) + find_lst = [f for f in container_1.finds.all()] + for f in [find0, find1, find2]: + self.assertIn(f, find_lst) diff --git a/archaeological_warehouse/urls.py b/archaeological_warehouse/urls.py index 6519a5db4..5c08af2ad 100644 --- a/archaeological_warehouse/urls.py +++ b/archaeological_warehouse/urls.py @@ -103,4 +103,13 @@ urlpatterns = [ url(r'^container-qa-lock/(?P<pks>[0-9-]+)?/$', views.QAContainerLockView.as_view(), name='container-qa-lock', kwargs={"model": models.Container}), + + url(r'container-merge/(?:(?P<page>\d+)/)?$', views.container_merge, + name='container_merge'), + url(r'container-manual-merge/$', + views.ContainerManualMerge.as_view(), + name='container_manual_merge'), + url(r'container-manual-merge-items/(?P<pks>[0-9_]+?)/$', + views.ContainerManualMergeItems.as_view(), + name='container_manual_merge_items'), ] diff --git a/archaeological_warehouse/views.py b/archaeological_warehouse/views.py index 29f33dc04..cb74b49f9 100644 --- a/archaeological_warehouse/views.py +++ b/archaeological_warehouse/views.py @@ -21,21 +21,19 @@ import json from django.core.urlresolvers import reverse from django.db.models import Q +from django.views.generic.edit import FormView from django.http import HttpResponse, Http404, HttpResponseRedirect from django.shortcuts import redirect from ishtar_common.utils import ugettext_lazy as _ from archaeological_warehouse import models +from archaeological_warehouse import forms -from archaeological_warehouse.forms import WarehouseForm, ContainerForm, \ - ContainerFormSelection, BasePackagingForm, WarehouseFormSelection, \ - WarehouseModifyForm, SelectedDivisionFormset, WarehouseDeletionForm, \ - MainContainerFormSelection, ContainerModifyForm, LocalisationForm, \ - ContainerDeletionForm, ContainerSelect, WarehouseSelect, \ - MainContainerFormMultiSelection, WarehouseFormMultiSelection from ishtar_common.forms import FinalForm -from ishtar_common.views import QABaseLockView, wizard_is_available +from ishtar_common.views import QABaseLockView, wizard_is_available, \ + merge_action, ManualMergeMixin, ManualMergeItemsMixin, IshtarMixin, \ + LoginRequiredMixin from ishtar_common.views_item import get_item, show_item, new_qa_item from archaeological_finds.views import treatment_add @@ -44,18 +42,17 @@ from archaeological_warehouse.wizards import PackagingWizard, WarehouseSearch, \ ContainerSearch, ContainerWizard, ContainerModificationWizard, \ ContainerDeletionWizard -from ishtar_common.utils import put_session_message get_container = get_item(models.Container, 'get_container', 'container', - search_form=ContainerSelect) + search_form=forms.ContainerSelect) show_container = show_item(models.Container, 'container') get_warehouse = get_item(models.Warehouse, 'get_warehouse', 'warehouse', - search_form=WarehouseSelect) + search_form=forms.WarehouseSelect) show_warehouse = show_item(models.Warehouse, 'warehouse') -new_warehouse = new_qa_item(models.Warehouse, WarehouseForm) -new_container = new_qa_item(models.Container, ContainerForm) +new_warehouse = new_qa_item(models.Warehouse, forms.WarehouseForm) +new_container = new_qa_item(models.Container, forms.ContainerForm) def autocomplete_warehouse(request): @@ -115,36 +112,36 @@ def autocomplete_container(request): return HttpResponse(data, content_type='text/plain') warehouse_packaging_wizard = PackagingWizard.as_view([ # AFAC - ('seleccontainer-packaging', ContainerFormSelection), - ('base-packaging', BasePackagingForm), + ('seleccontainer-packaging', forms.ContainerFormSelection), + ('base-packaging', forms.BasePackagingForm), ('final-packaging', FinalForm)], - label=_(u"Packaging"), + label=_("Packaging"), url_name='warehouse_packaging',) warehouse_search_wizard = WarehouseSearch.as_view([ - ('selec-warehouse_search', WarehouseFormSelection)], - label=_(u"Warehouse search"), + ('selec-warehouse_search', forms.WarehouseFormSelection)], + label=_("Warehouse search"), url_name='warehouse_search', ) warehouse_creation_steps = [ - ("warehouse-warehouse_creation", WarehouseForm), - ('divisions-warehouse_creation', SelectedDivisionFormset), + ("warehouse-warehouse_creation", forms.WarehouseForm), + ('divisions-warehouse_creation', forms.SelectedDivisionFormset), ('final-warehouse_creation', FinalForm)] warehouse_creation_wizard = WarehouseWizard.as_view( warehouse_creation_steps, - label=_(u"Warehouse creation"), + label=_("Warehouse creation"), url_name='warehouse_creation', ) warehouse_modification_wizard = WarehouseModificationWizard.as_view([ - ('selec-warehouse_modification', WarehouseFormSelection), - ("warehouse-warehouse_modification", WarehouseModifyForm), - ('divisions-warehouse_modification', SelectedDivisionFormset), + ('selec-warehouse_modification', forms.WarehouseFormSelection), + ("warehouse-warehouse_modification", forms.WarehouseModifyForm), + ('divisions-warehouse_modification', forms.SelectedDivisionFormset), ('final-warehouse_modification', FinalForm)], - label=_(u"Warehouse modification"), + label=_("Warehouse modification"), url_name='warehouse_modification', ) @@ -161,9 +158,9 @@ def warehouse_modify(request, pk): warehouse_deletion_wizard = WarehouseDeletionWizard.as_view([ - ('selec-warehouse_deletion', WarehouseFormMultiSelection), - ('final-warehouse_deletion', WarehouseDeletionForm)], - label=_(u"Warehouse deletion"), + ('selec-warehouse_deletion', forms.WarehouseFormMultiSelection), + ('final-warehouse_deletion', forms.WarehouseDeletionForm)], + label=_("Warehouse deletion"), url_name='warehouse_deletion',) @@ -184,28 +181,28 @@ class QAWarehouseLockView(QABaseLockView): container_search_wizard = ContainerSearch.as_view([ - ('selec-container_search', MainContainerFormSelection)], - label=_(u"Container search"), + ('selec-container_search', forms.MainContainerFormSelection)], + label=_("Container search"), url_name='container_search', ) container_creation_steps = [ - ('container-container_creation', ContainerForm), - ('localisation-container_creation', LocalisationForm), + ('container-container_creation', forms.ContainerForm), + ('localisation-container_creation', forms.LocalisationForm), ('final-container_creation', FinalForm)] container_creation_wizard = ContainerWizard.as_view( container_creation_steps, - label=_(u"Container creation"), + label=_("Container creation"), url_name='container_creation', ) container_modification_wizard = ContainerModificationWizard.as_view([ - ('selec-container_modification', MainContainerFormSelection), - ('container-container_modification', ContainerModifyForm), - ('localisation-container_modification', LocalisationForm), + ('selec-container_modification', forms.MainContainerFormSelection), + ('container-container_modification', forms.ContainerModifyForm), + ('localisation-container_modification', forms.LocalisationForm), ('final-container_modification', FinalForm)], - label=_(u"Container modification"), + label=_("Container modification"), url_name='container_modification', ) @@ -222,9 +219,9 @@ def container_modify(request, pk): container_deletion_wizard = ContainerDeletionWizard.as_view([ - ('selec-container_deletion', MainContainerFormMultiSelection), - ('final-container_deletion', ContainerDeletionForm)], - label=_(u"Container deletion"), + ('selec-container_deletion', forms.MainContainerFormMultiSelection), + ('final-container_deletion', forms.ContainerDeletionForm)], + label=_("Container deletion"), url_name='container_deletion',) @@ -255,6 +252,28 @@ warehouse_packaging_wizard = ItemSourceWizard.as_view([ """ +container_merge = merge_action(models.Container, forms.MergeContainerForm, + 'container', name_key="reference") + + +class ContainerManualMerge(ManualMergeMixin, IshtarMixin, LoginRequiredMixin, + FormView): + form_class = forms.ContainerMergeFormSelection + template_name = 'ishtar/form.html' + page_name = _("Merge containers") + current_url = 'container-manual-merge' + redir_url = 'container_manual_merge_items' + + +class ContainerManualMergeItems(ManualMergeItemsMixin, IshtarMixin, + LoginRequiredMixin, FormView): + form_class = forms.ContainerMergeIntoForm + template_name = 'ishtar/form.html' + page_name = _("Select the main container") + current_url = 'container-manual-merge-items' + item_type = 'container' + + class QAContainerLockView(QABaseLockView): model = models.Container base_url = "container-qa-lock" diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 390672b51..df8535240 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -60,13 +60,13 @@ from archaeological_warehouse.models import Container def get_town_field(label=_("Town"), required=True): help_text = _( - u"<p>Type name, department code of the " - u"town you would like to select. The search is insensitive to case." - u"</p>\n<p>Only the first twenty results are displayed but specifying " - u"the department code is generally sufficient to get the appropriate " - u"result.</p>\n<p class='example'>For instance type \"saint denis 93\"" - u" for getting the french town Saint-Denis in the Seine-Saint-Denis " - u"department.</p>") + "<p>Type name, department code of the " + "town you would like to select. The search is insensitive to case." + "</p>\n<p>Only the first twenty results are displayed but specifying " + "the department code is generally sufficient to get the appropriate " + "result.</p>\n<p class='example'>For instance type \"saint denis 93\"" + " for getting the french town Saint-Denis in the Seine-Saint-Denis " + "department.</p>") # !FIXME hard_link, reverse_lazy doen't seem to work with formsets return forms.IntegerField( widget=widgets.JQueryAutoComplete( @@ -76,7 +76,7 @@ def get_town_field(label=_("Town"), required=True): help_text=mark_safe(help_text), required=required) -def get_advanced_town_field(label=_(u"Town"), required=True): +def get_advanced_town_field(label=_("Town"), required=True): # !FIXME hard_link, reverse_lazy doen't seem to work with formsets return forms.IntegerField( widget=widgets.JQueryTown( @@ -85,7 +85,7 @@ def get_advanced_town_field(label=_(u"Town"), required=True): required=required) -def get_person_field(label=_(u"Person"), required=True, person_types=[]): +def get_person_field(label=_("Person"), required=True, person_types=[]): # !FIXME hard_link, reverse_lazy doen't seem to work with formsets widget = None url = "/" + settings.URL_PATH + 'autocomplete-person' @@ -93,7 +93,7 @@ def get_person_field(label=_(u"Person"), required=True, person_types=[]): person_types = [ str(models.PersonType.objects.get(txt_idx=person_type).pk) for person_type in person_types] - url += u"/" + u'_'.join(person_types) + url += "/" + u'_'.join(person_types) widget = widgets.JQueryAutoComplete(url, associated_model=models.Person) return forms.IntegerField(widget=widget, label=label, required=required, validators=[models.valid_id(models.Person)]) @@ -126,7 +126,7 @@ class NewImportForm(BSForm, forms.ModelForm): error_css_class = 'error' required_css_class = 'required' imported_images_link = forms.URLField( - label=_(u"Associated images (web link to a zip file)"), + label=_("Associated images (web link to a zip file)"), required=False ) @@ -164,13 +164,13 @@ class NewImportForm(BSForm, forms.ModelForm): and data.get('importer_type') \ and not data.get('importer_type').unicity_keys: raise forms.ValidationError( - _(u"This import type have no unicity type defined. " - u"Conservative import is not possible.")) + _("This import type have no unicity type defined. " + "Conservative import is not possible.")) if data.get('imported_images_link', None) \ and data.get('imported_images', None): raise forms.ValidationError( - _(u"You put either a file or a download link for images " - u"but not both.")) + _("You put either a file or a download link for images " + "but not both.")) return data def clean_imported_images_link(self): @@ -180,7 +180,7 @@ class NewImportForm(BSForm, forms.ModelForm): assert is_downloadable(value) except (AssertionError, requests.exceptions.RequestException): raise forms.ValidationError( - _(u"Invalid link or no file is available for this link.")) + _("Invalid link or no file is available for this link.")) return value def save(self, user, commit=True): @@ -209,11 +209,11 @@ class TargetKeyForm(forms.ModelForm): 'key': forms.TextInput(attrs={'readonly': 'readonly'}), } target = widgets.SelectReadonlyField( - model=models.ImportTarget, label=_(u"Target")) + model=models.ImportTarget, label=_("Target")) value = widgets.Select2SimpleField( - label=_(u"Value"), required=False + label=_("Value"), required=False ) - remember = forms.ChoiceField(label=_(u"Remember"), choices=[], + remember = forms.ChoiceField(label=_("Remember"), choices=[], required=False) NULL_VALUE = '<NONE>' @@ -238,14 +238,14 @@ class TargetKeyForm(forms.ModelForm): self.fields['value'].required = False choices = [ - ('import', _(u"this import only")), - ('me', _(u"me")), + ('import', _("this import only")), + ('me', _("me")), ] if self.associated_import and self.associated_import.associated_group \ and (self.associated_import.associated_group.all_user_can_modify or self.user.is_superuser): choices += [ - ('group', str(_(u"the current group: {}")).format( + ('group', str(_("the current group: {}")).format( self.associated_import.associated_group))] if self.user.is_superuser: choices += [('all', _("all users"))] @@ -323,24 +323,24 @@ class OrganizationForm(ManageOldType, NewItemForm): associated_models = {'organization_type': models.OrganizationType, "precise_town": models.Town} name = forms.CharField( - label=_(u"Name"), max_length=300, validators=[name_validator]) - organization_type = forms.ChoiceField(label=_(u"Organization type"), + label=_("Name"), max_length=300, validators=[name_validator]) + organization_type = forms.ChoiceField(label=_("Organization type"), choices=[]) url = forms.URLField(label=_("Web address"), required=False) - address = forms.CharField(label=_(u"Address"), widget=forms.Textarea, + address = forms.CharField(label=_("Address"), widget=forms.Textarea, required=False) - address_complement = forms.CharField(label=_(u"Address complement"), + address_complement = forms.CharField(label=_("Address complement"), widget=forms.Textarea, required=False) - postal_code = forms.CharField(label=_(u"Postal code"), max_length=10, + postal_code = forms.CharField(label=_("Postal code"), max_length=10, required=False) town = forms.CharField(label=_("Town (freeform)"), max_length=30, required=False) precise_town = get_town_field(required=False) - country = forms.CharField(label=_(u"Country"), max_length=30, + country = forms.CharField(label=_("Country"), max_length=30, required=False) - email = forms.EmailField(label=_(u"Email"), required=False) - phone = forms.CharField(label=_(u"Phone"), max_length=18, required=False) - mobile_phone = forms.CharField(label=_(u"Mobile phone"), max_length=18, + email = forms.EmailField(label=_("Email"), required=False) + phone = forms.CharField(label=_("Phone"), max_length=18, required=False) + mobile_phone = forms.CharField(label=_("Mobile phone"), max_length=18, required=False) def __init__(self, *args, **kwargs): @@ -377,11 +377,11 @@ class OrganizationSelect(CustomForm, TableSelect): _model = models.Organization search_vector = forms.CharField( - label=_(u"Full text search"), widget=widgets.SearchWidget( + label=_("Full text search"), widget=widgets.SearchWidget( 'ishtar-common', 'organization' )) - name = forms.CharField(label=_(u"Name"), max_length=300) - organization_type = forms.ChoiceField(label=_(u"Type"), choices=[]) + name = forms.CharField(label=_("Name"), max_length=300) + organization_type = forms.ChoiceField(label=_("Type"), choices=[]) def __init__(self, *args, **kwargs): super(OrganizationSelect, self).__init__(*args, **kwargs) @@ -391,7 +391,7 @@ class OrganizationSelect(CustomForm, TableSelect): class OrganizationFormSelection(CustomFormSearch): SEARCH_AND_SELECT = True - form_label = _(u"Organization search") + form_label = _("Organization search") associated_models = {'pk': models.Organization} currents = {'pk': models.Organization} pk = forms.IntegerField( @@ -404,7 +404,7 @@ class OrganizationFormSelection(CustomFormSearch): class OrganizationFormMultiSelection(MultiSearchForm): - form_label = _(u"Organization search") + form_label = _("Organization search") associated_models = {'pks': models.Organization} pk = forms.CharField( label="", @@ -418,7 +418,7 @@ class OrganizationFormMultiSelection(MultiSearchForm): class QAOrganizationFormMulti(QAForm): - form_admin_name = _(u"Organization - Quick action - Modify") + form_admin_name = _("Organization - Quick action - Modify") form_slug = "organization-quickaction-modify" base_models = ['qa_organization_type'] associated_models = { @@ -430,7 +430,7 @@ class QAOrganizationFormMulti(QAForm): 'qa_organization_type', ] qa_organization_type = forms.ChoiceField( - label=_(u"Organization type"), required=False + label=_("Organization type"), required=False ) TYPES = [ @@ -450,8 +450,8 @@ class ManualMerge(object): except ValueError: pass if len(values) < 2: - raise forms.ValidationError(_(u"At least two items have to be " - u"selected.")) + raise forms.ValidationError(_("At least two items have to be " + "selected.")) self.cleaned_data['to_merge'] = values return values @@ -481,8 +481,8 @@ class MergeIntoForm(forms.Form): except self.associated_model.DoesNotExist: continue self.fields['main_item'].choices.append( - (item.pk, mark_safe(u"{} {}".format(simple_link_to_window(item), - str(item))))) + (item.pk, mark_safe("{} {}".format(simple_link_to_window(item), + str(item))))) def merge(self): model = self.associated_model @@ -503,7 +503,7 @@ class MergeIntoForm(forms.Form): class OrgaMergeFormSelection(ManualMerge, forms.Form): SEARCH_AND_SELECT = True - form_label = _(u"Organization to merge") + form_label = _("Organization to merge") associated_models = {'to_merge': models.Organization} currents = {'to_merge': models.Organization} to_merge = forms.CharField( @@ -532,13 +532,13 @@ class PersonSelect(CustomForm, TableSelect): _model = models.Person search_vector = forms.CharField( - label=_(u"Full text search"), widget=widgets.SearchWidget( + label=_("Full text search"), widget=widgets.SearchWidget( 'ishtar-common', 'person' )) - name = forms.CharField(label=_(u"Name"), max_length=200) - surname = forms.CharField(label=_(u"Surname"), max_length=50) - email = forms.CharField(label=_(u"Email"), max_length=75) - person_types = forms.ChoiceField(label=_(u"Type"), choices=[]) + name = forms.CharField(label=_("Name"), max_length=200) + surname = forms.CharField(label=_("Surname"), max_length=50) + email = forms.CharField(label=_("Email"), max_length=75) + person_types = forms.ChoiceField(label=_("Type"), choices=[]) attached_to = forms.IntegerField( label=_("Organization"), widget=widgets.JQueryAutoComplete( @@ -553,7 +553,7 @@ class PersonSelect(CustomForm, TableSelect): class PersonFormSelection(CustomFormSearch): SEARCH_AND_SELECT = True - form_label = _(u"Person search") + form_label = _("Person search") associated_models = {'pk': models.Person} currents = {'pk': models.Person} pk = forms.IntegerField( @@ -565,7 +565,7 @@ class PersonFormSelection(CustomFormSearch): class PersonFormMultiSelection(MultiSearchForm): - form_label = _(u"Person search") + form_label = _("Person search") associated_models = {'pks': models.Person} pk = forms.CharField( @@ -579,7 +579,7 @@ class PersonFormMultiSelection(MultiSearchForm): class QAPersonFormMulti(QAForm): - form_admin_name = _(u"Person - Quick action - Modify") + form_admin_name = _("Person - Quick action - Modify") form_slug = "person-quickaction-modify" base_models = ['qa_title'] associated_models = { @@ -593,7 +593,7 @@ class QAPersonFormMulti(QAForm): 'qa_attached_to' ] qa_title = forms.ChoiceField( - label=_(u"Title"), required=False + label=_("Title"), required=False ) qa_attached_to = forms.IntegerField( label=_("Organization"), @@ -643,25 +643,25 @@ class SimplePersonForm(ManageOldType, NewItemForm): title = forms.ChoiceField(label=_("Title"), choices=[], required=False) salutation = forms.CharField(label=_("Salutation"), max_length=200, required=False) - surname = forms.CharField(label=_(u"Surname"), max_length=50, + surname = forms.CharField(label=_("Surname"), max_length=50, validators=[name_validator]) - name = forms.CharField(label=_(u"Name"), max_length=200, + name = forms.CharField(label=_("Name"), max_length=200, validators=[name_validator]) - raw_name = forms.CharField(label=_(u"Raw name"), max_length=300, + raw_name = forms.CharField(label=_("Raw name"), max_length=300, required=False) - email = forms.EmailField(label=_(u"Email"), required=False) - phone_desc = forms.CharField(label=_(u"Phone description"), max_length=300, + email = forms.EmailField(label=_("Email"), required=False) + phone_desc = forms.CharField(label=_("Phone description"), max_length=300, required=False) - phone = forms.CharField(label=_(u"Phone"), max_length=18, required=False) - phone_desc2 = forms.CharField(label=_(u"Phone description 2"), + phone = forms.CharField(label=_("Phone"), max_length=18, required=False) + phone_desc2 = forms.CharField(label=_("Phone description 2"), max_length=300, required=False) - phone2 = forms.CharField(label=_(u"Phone 2"), max_length=18, + phone2 = forms.CharField(label=_("Phone 2"), max_length=18, required=False) - phone_desc3 = forms.CharField(label=_(u"Phone description 3"), + phone_desc3 = forms.CharField(label=_("Phone description 3"), max_length=300, required=False) - phone3 = forms.CharField(label=_(u"Phone 3"), max_length=18, + phone3 = forms.CharField(label=_("Phone 3"), max_length=18, required=False) - mobile_phone = forms.CharField(label=_(u"Mobile phone"), max_length=18, + mobile_phone = forms.CharField(label=_("Mobile phone"), max_length=18, required=False) attached_to = forms.IntegerField( label=_("Current organization"), @@ -669,27 +669,27 @@ class SimplePersonForm(ManageOldType, NewItemForm): reverse_lazy('autocomplete-organization'), associated_model=models.Organization, new=True), validators=[models.valid_id(models.Organization)], required=False) - address = forms.CharField(label=_(u"Address"), widget=forms.Textarea, + address = forms.CharField(label=_("Address"), widget=forms.Textarea, required=False) address_complement = forms.CharField( - label=_(u"Address complement"), widget=forms.Textarea, required=False) - postal_code = forms.CharField(label=_(u"Postal code"), max_length=10, + label=_("Address complement"), widget=forms.Textarea, required=False) + postal_code = forms.CharField(label=_("Postal code"), max_length=10, required=False) town = forms.CharField(label=_("Town (freeform)"), max_length=30, required=False) precise_town = get_town_field(required=False) - country = forms.CharField(label=_(u"Country"), max_length=30, + country = forms.CharField(label=_("Country"), max_length=30, required=False) - alt_address = forms.CharField(label=_(u"Other address: address"), + alt_address = forms.CharField(label=_("Other address: address"), widget=forms.Textarea, required=False) alt_address_complement = forms.CharField( - label=_(u"Other address: address complement"), + label=_("Other address: address complement"), widget=forms.Textarea, required=False) - alt_postal_code = forms.CharField(label=_(u"Other address: postal code"), + alt_postal_code = forms.CharField(label=_("Other address: postal code"), max_length=10, required=False) - alt_town = forms.CharField(label=_(u"Other address: town"), max_length=30, + alt_town = forms.CharField(label=_("Other address: town"), max_length=30, required=False) - alt_country = forms.CharField(label=_(u"Other address: country"), + alt_country = forms.CharField(label=_("Other address: country"), max_length=30, required=False) def __init__(self, *args, **kwargs): @@ -701,12 +701,12 @@ class SimplePersonForm(ManageOldType, NewItemForm): class PersonUserSelect(PersonSelect): ishtaruser__isnull = forms.NullBooleanField( - label=_(u"Already has an account")) + label=_("Already has an account")) class PersonUserFormSelection(PersonFormSelection): SEARCH_AND_SELECT = True - form_label = _(u"Person search") + form_label = _("Person search") associated_models = {'pk': models.Person} currents = {'pk': models.Person} pk = forms.IntegerField( @@ -721,14 +721,14 @@ class IshtarUserSelect(TableSelect): _model = models.IshtarUser search_vector = forms.CharField( - label=_(u"Full text search"), widget=widgets.SearchWidget( + label=_("Full text search"), widget=widgets.SearchWidget( 'ishtar-common', 'ishtaruser' )) - username = forms.CharField(label=_(u"Username"), max_length=200) - name = forms.CharField(label=_(u"Name"), max_length=200) - surname = forms.CharField(label=_(u"Surname"), max_length=50) - email = forms.CharField(label=_(u"Email"), max_length=75) - person_types = forms.ChoiceField(label=_(u"Type"), choices=[]) + username = forms.CharField(label=_("Username"), max_length=200) + name = forms.CharField(label=_("Name"), max_length=200) + surname = forms.CharField(label=_("Surname"), max_length=50) + email = forms.CharField(label=_("Email"), max_length=75) + person_types = forms.ChoiceField(label=_("Type"), choices=[]) attached_to = forms.IntegerField( label=_("Organization"), widget=widgets.JQueryAutoComplete( @@ -743,7 +743,7 @@ class IshtarUserSelect(TableSelect): class AccountFormSelection(forms.Form): SEARCH_AND_SELECT = True - form_label = _(u"Account search") + form_label = _("Account search") associated_models = {'pk': models.IshtarUser} currents = {'pk': models.IshtarUser} pk = forms.IntegerField( @@ -854,21 +854,21 @@ class AccountForm(IshtarForm): form_label = _("Account") associated_models = {'pk': models.Person} currents = {'pk': models.Person} - pk = forms.IntegerField(label=u"", widget=forms.HiddenInput, + pk = forms.IntegerField(label="", widget=forms.HiddenInput, required=False) - username = forms.CharField(label=_(u"Account"), max_length=30) - email = forms.CharField(label=_(u"Email"), max_length=75, + username = forms.CharField(label=_("Account"), max_length=30) + email = forms.CharField(label=_("Email"), max_length=75, validators=[validators.validate_email]) hidden_password = forms.CharField( - label=_(u"New password"), max_length=128, widget=forms.PasswordInput, + label=_("New password"), max_length=128, widget=forms.PasswordInput, required=False, validators=[validators.MinLengthValidator(4)]) hidden_password_confirm = forms.CharField( - label=_(u"New password (confirmation)"), max_length=128, + label=_("New password (confirmation)"), max_length=128, widget=forms.PasswordInput, required=False) HEADERS = { 'hidden_password': FormHeader( - _(u"New password"), + _("New password"), help_message=_("Keep theses fields empty if you do not want to " "change password. On creation, if you leave these " "fields empty, the user will not be able to " @@ -905,20 +905,20 @@ class AccountForm(IshtarForm): password = cleaned_data.get("hidden_password") if password and \ password != cleaned_data.get("hidden_password_confirm"): - raise forms.ValidationError(_(u"Your password and confirmation " - u"password do not match.")) + raise forms.ValidationError(_("Your password and confirmation " + "password do not match.")) if not cleaned_data.get("pk"): models.is_unique(User, 'username')(cleaned_data.get("username")) if not password: - raise forms.ValidationError(_(u"You must provide a correct " - u"password.")) + raise forms.ValidationError(_("You must provide a correct " + "password.")) # check username unicity q = models.IshtarUser.objects.filter( user_ptr__username=cleaned_data.get('username')) if cleaned_data.get('pk'): q = q.exclude(person__pk=cleaned_data.get('pk')) if q.count(): - raise forms.ValidationError(_(u"This username already exists.")) + raise forms.ValidationError(_("This username already exists.")) return cleaned_data @@ -929,9 +929,9 @@ class ProfileForm(ManageOldType): 'profile_type': models.ProfileType, 'area': models.Area } - profile_type = forms.ChoiceField(label=_(u"Type"), choices=[]) - area = widgets.Select2MultipleField(label=_(u"Areas"), required=False) - name = forms.CharField(label=_(u"Name"), required=False) + profile_type = forms.ChoiceField(label=_("Type"), choices=[]) + area = widgets.Select2MultipleField(label=_("Areas"), required=False) + name = forms.CharField(label=_("Name"), required=False) pk = forms.IntegerField(label=" ", widget=forms.HiddenInput, required=False) TYPES = [ @@ -943,15 +943,15 @@ class ProfileForm(ManageOldType): ProfileFormset = formset_factory(ProfileForm, can_delete=True, formset=FormSetWithDeleteSwitches) ProfileFormset.form_label = _("Profiles") -ProfileFormset.form_admin_name = _(u"Profiles") +ProfileFormset.form_admin_name = _("Profiles") ProfileFormset.form_slug = "profiles" class FinalAccountForm(forms.Form): final = True form_label = _("Confirm") - send_password = forms.BooleanField(label=_(u"Send the new password by " - u"email?"), required=False) + send_password = forms.BooleanField(label=_("Send the new password by " + "email?"), required=False) def __init__(self, *args, **kwargs): self.is_hidden = True @@ -962,22 +962,22 @@ class ProfilePersonForm(forms.Form): """ Edit the current profile """ - current_profile = forms.ChoiceField(label=_(u"Current profile"), + current_profile = forms.ChoiceField(label=_("Current profile"), choices=[]) - name = forms.CharField(label=_(u"Name"), required=False) - profile_type = forms.ChoiceField(label=_(u"Profile type"), required=False, + name = forms.CharField(label=_("Name"), required=False) + profile_type = forms.ChoiceField(label=_("Profile type"), required=False, disabled=True, choices=[]) auto_pin = forms.BooleanField( - label=_(u"Pin automatically items on creation and modification"), + label=_("Pin automatically items on creation and modification"), required=False) display_pin_menu = forms.BooleanField( - label=_(u"Show pin menu"), + label=_("Show pin menu"), required=False) duplicate_profile = forms.BooleanField( - label=_(u"Duplicate this profile"), required=False) + label=_("Duplicate this profile"), required=False) delete_profile = forms.BooleanField( - label=_(u"Delete this profile"), required=False, + label=_("Delete this profile"), required=False, ) def __init__(self, *args, **kwargs): @@ -1028,7 +1028,7 @@ class ProfilePersonForm(forms.Form): person__ishtaruser=self.user.ishtaruser, name=name).exclude(pk=profile.pk).count(): raise forms.ValidationError( - _(u"A profile with the same name exists.")) + _("A profile with the same name exists.")) return data def save(self, session): @@ -1060,7 +1060,7 @@ class ProfilePersonForm(forms.Form): if self.cleaned_data.get('duplicate_profile', None): profile_name = profile.name or profile.profile_type.label if name == profile_name: - name += str(_(u" (duplicate)")) + name += str(_(" (duplicate)")) profile.duplicate(name=name) return @@ -1088,7 +1088,7 @@ class TownFormSet(FormSet): TownFormset = formset_factory(TownForm, can_delete=True, formset=TownFormSet) TownFormset.form_label = _("Towns") -TownFormset.form_admin_name = _(u"Towns") +TownFormset.form_admin_name = _("Towns") TownFormset.form_slug = "towns" @@ -1161,7 +1161,7 @@ class MergeFormSet(BaseModelFormSet): class MergeForm(forms.ModelForm): id = forms.IntegerField( - label=u"", widget=forms.HiddenInput, required=False) + label="", widget=forms.HiddenInput, required=False) a_is_duplicate_b = forms.BooleanField(required=False) b_is_duplicate_a = forms.BooleanField(required=False) not_duplicate = forms.BooleanField(required=False) @@ -1170,7 +1170,7 @@ class MergeForm(forms.ModelForm): checked = [True for k in ['a_is_duplicate_b', 'b_is_duplicate_a', 'not_duplicate'] if self.cleaned_data.get(k)] if len(checked) > 1: - raise forms.ValidationError(_(u"Only one choice can be checked.")) + raise forms.ValidationError(_("Only one choice can be checked.")) return self.cleaned_data def merge(self, *args, **kwargs): @@ -1218,11 +1218,10 @@ def get_image_help(): if not settings.IMAGE_MAX_SIZE: return max_size_help() return str( - _(u"Heavy images are resized to: %(width)dx%(height)d " - u"(ratio is preserved).") \ - % {'width': settings.IMAGE_MAX_SIZE[0], - 'height': settings.IMAGE_MAX_SIZE[1]}) + " " + str( - max_size_help()) + _("Heavy images are resized to: %(width)dx%(height)d " + "(ratio is preserved).") % { + 'width': settings.IMAGE_MAX_SIZE[0], + 'height': settings.IMAGE_MAX_SIZE[1]}) + " " + str(max_size_help()) ####################### @@ -1231,7 +1230,7 @@ def get_image_help(): class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): - form_label = _(u"Documentation") + form_label = _("Documentation") form_admin_name = _("Document - General") form_slug = "document-general" file_upload = True @@ -1239,48 +1238,48 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): associated_models = {'source_type': models.SourceType} pk = forms.IntegerField(label="", required=False, widget=forms.HiddenInput) - title = forms.CharField(label=_(u"Title"), required=False, + title = forms.CharField(label=_("Title"), required=False, validators=[validators.MaxLengthValidator(200)]) source_type = widgets.ModelChoiceField( - model=models.SourceType, label=_(u"Source type"), choices=[], + model=models.SourceType, label=_("Source type"), choices=[], required=False) authors = widgets.ModelJQueryAutocompleteField( - model=models.Author, multiple=True, label=_(u"Authors"), new=True, + model=models.Author, multiple=True, label=_("Authors"), new=True, long_widget=True, required=False) associated_url = forms.URLField( max_length=1000, required=False, - label=_(u"Numerical ressource (web address)")) + label=_("Numerical ressource (web address)")) image = forms.ImageField( - label=_(u"Image"), help_text=mark_safe(get_image_help()), + label=_("Image"), help_text=mark_safe(get_image_help()), max_length=255, required=False, widget=widgets.ImageFileInput(), validators=[file_size_validator] ) associated_file = forms.FileField( - label=pgettext(u"Not directory", u"File"), max_length=255, + label=pgettext("Not directory", "File"), max_length=255, required=False, help_text=max_size_help(), validators=[file_size_validator] ) reference = forms.CharField( - label=_(u"Reference"), + label=_("Reference"), validators=[validators.MaxLengthValidator(100)], required=False) internal_reference = forms.CharField( - label=_(u"Internal reference"), + label=_("Internal reference"), validators=[validators.MaxLengthValidator(100)], required=False) - receipt_date = forms.DateField(label=_(u"Receipt date"), required=False, + receipt_date = forms.DateField(label=_("Receipt date"), required=False, widget=DatePicker) - creation_date = forms.DateField(label=_(u"Creation date"), required=False, + creation_date = forms.DateField(label=_("Creation date"), required=False, widget=DatePicker) receipt_date_in_documentation = forms.DateField( - label=_(u"Receipt date in documentation"), required=False, + label=_("Receipt date in documentation"), required=False, widget=DatePicker) - comment = forms.CharField(label=_(u"Comment"), widget=forms.Textarea, + comment = forms.CharField(label=_("Comment"), widget=forms.Textarea, required=False) - description = forms.CharField(label=_(u"Description"), + description = forms.CharField(label=_("Description"), widget=forms.Textarea, required=False) additional_information = forms.CharField( - label=_(u"Additional information"), widget=forms.Textarea, + label=_("Additional information"), widget=forms.Textarea, required=False) - duplicate = forms.NullBooleanField(label=_(u"Has a duplicate"), + duplicate = forms.NullBooleanField(label=_("Has a duplicate"), required=False) TYPES = [ @@ -1298,12 +1297,12 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): ] HEADERS = { - 'title': FormHeader(_(u"Identification")), - 'image': FormHeader(_(u"Content")), - 'authors': FormHeader(_(u"Authors")), - 'receipt_date': FormHeader(_(u"Dates")), - 'comment': FormHeader(_(u"Advanced"), collapse=True), - 'finds': FormHeader(_(u"Related items")), + 'title': FormHeader(_("Identification")), + 'image': FormHeader(_("Content")), + 'authors': FormHeader(_("Authors")), + 'receipt_date': FormHeader(_("Dates")), + 'comment': FormHeader(_("Advanced"), collapse=True), + 'finds': FormHeader(_("Related items")), } def __init__(self, *args, **kwargs): @@ -1341,8 +1340,8 @@ 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 has to be attached at least " - u"to one item")) + raise forms.ValidationError(_("A document has to be attached at least " + "to one item")) def save(self, commit=True): if not self.cleaned_data.get('authors', None): @@ -1378,38 +1377,38 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): class DocumentSelect(HistorySelect): _model = models.Document - form_admin_name = _(u"Document - 001 - Search") + form_admin_name = _("Document - 001 - Search") form_slug = "document-001-search" search_vector = forms.CharField( - label=_(u"Full text search"), widget=widgets.SearchWidget( + label=_("Full text search"), widget=widgets.SearchWidget( 'ishtar-common', 'document' )) authors = forms.IntegerField( widget=widgets.JQueryAutoComplete( "/" + settings.URL_PATH + 'autocomplete-author', associated_model=models.Author), - validators=[models.valid_id(models.Author)], label=_(u"Author"), + validators=[models.valid_id(models.Author)], label=_("Author"), required=False) - title = forms.CharField(label=_(u"Title")) + title = forms.CharField(label=_("Title")) source_type = forms.ChoiceField(label=_("Source type"), choices=[]) - reference = forms.CharField(label=_(u"Reference")) - internal_reference = forms.CharField(label=_(u"Internal reference")) - description = forms.CharField(label=_(u"Description")) - comment = forms.CharField(label=_(u"Comment")) + reference = forms.CharField(label=_("Reference")) + internal_reference = forms.CharField(label=_("Internal reference")) + description = forms.CharField(label=_("Description")) + comment = forms.CharField(label=_("Comment")) additional_information = forms.CharField( - label=_(u"Additional informations")) - duplicate = forms.NullBooleanField(label=_(u"Has a duplicate")) - image__isnull = forms.NullBooleanField(label=_(u"Has an image?")) + label=_("Additional informations")) + duplicate = forms.NullBooleanField(label=_("Has a duplicate")) + image__isnull = forms.NullBooleanField(label=_("Has an image?")) operation = forms.IntegerField( - label=_(u"Operation"), required=False, + label=_("Operation"), required=False, widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-operation'), associated_model=Operation), validators=[models.valid_id(Operation)]) context_record = forms.IntegerField( - label=_(u"Context record"), required=False, + label=_("Context record"), required=False, widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-contextrecord'), associated_model=ContextRecord), @@ -1421,15 +1420,15 @@ class DocumentSelect(HistorySelect): associated_model=FindBasket), validators=[models.valid_id(FindBasket)], required=False) find = forms.IntegerField( - label=_(u"Find"), required=False, + label=_("Find"), required=False, widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-find'), associated_model=Find), validators=[models.valid_id(Find)]) - find__denomination = forms.CharField(label=_(u"Find - denomination"), + find__denomination = forms.CharField(label=_("Find - denomination"), required=False) container = forms.IntegerField( - label=_(u"Container"), required=False, + label=_("Container"), required=False, widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-container'), associated_model=Container), @@ -1448,7 +1447,7 @@ class DocumentSelect(HistorySelect): class DocumentFormSelection(LockForm, CustomFormSearch): SEARCH_AND_SELECT = True - form_label = _(u"Document search") + form_label = _("Document search") associated_models = {'pk': models.Document} currents = {'pk': models.Document} @@ -1463,7 +1462,7 @@ class DocumentFormSelection(LockForm, CustomFormSearch): class DocumentFormMultiSelection(LockForm, MultiSearchForm): - form_label = _(u"Document search") + form_label = _("Document search") associated_models = {'pks': models.Document} pk_key = 'pks' @@ -1479,7 +1478,7 @@ class DocumentFormMultiSelection(LockForm, MultiSearchForm): class QADocumentFormMulti(QAForm): - form_admin_name = _(u"Document - Quick action - Modify") + form_admin_name = _("Document - Quick action - Modify") form_slug = "document-quickaction-modify" base_models = ['qa_source_type'] associated_models = { @@ -1493,10 +1492,10 @@ class QADocumentFormMulti(QAForm): 'qa_creation_date', ] qa_source_type = forms.ChoiceField( - label=_(u"Source type"), required=False + label=_("Source type"), required=False ) qa_authors = widgets.ModelJQueryAutocompleteField( - model=models.Author, label=_(u"Author"), new=True, + model=models.Author, label=_("Author"), new=True, required=False) qa_creation_date = forms.DateField( label=_("Creation date"), widget=DatePicker, required=False) @@ -1598,7 +1597,7 @@ class QALockForm(forms.Form): class SourceDeletionForm(FinalForm): confirm_msg = " " - confirm_end_msg = _(u"Would you like to delete this documentation?") + confirm_end_msg = _("Would you like to delete this documentation?") ###################### # Authors management # @@ -1606,15 +1605,15 @@ class SourceDeletionForm(FinalForm): class AuthorForm(ManageOldType, NewItemForm): - form_label = _(u"Author") + form_label = _("Author") associated_models = {'person': models.Person, 'author_type': models.AuthorType} person = forms.IntegerField( widget=widgets.JQueryAutoComplete( "/" + settings.URL_PATH + 'autocomplete-person', associated_model=models.Person, new=True), - validators=[models.valid_id(models.Person)], label=_(u"Person")) - author_type = forms.ChoiceField(label=_(u"Author type"), choices=[]) + validators=[models.valid_id(models.Person)], label=_("Person")) + author_type = forms.ChoiceField(label=_("Author type"), choices=[]) def __init__(self, *args, **kwargs): super(AuthorForm, self).__init__(*args, **kwargs) @@ -1642,7 +1641,7 @@ class AuthorForm(ManageOldType, NewItemForm): class AuthorFormSelection(forms.Form): - form_label = _(u"Author selection") + form_label = _("Author selection") base_model = 'author' associated_models = {'author': models.Author} author = forms.IntegerField( @@ -1650,7 +1649,7 @@ class AuthorFormSelection(forms.Form): widget=widgets.JQueryAutoComplete( "/" + settings.URL_PATH + 'autocomplete-author', associated_model=models.Author, new=True), - validators=[models.valid_id(models.Author)], label=_(u"Author")) + validators=[models.valid_id(models.Author)], label=_("Author")) class AuthorFormSet(FormSet): @@ -1663,20 +1662,20 @@ class AuthorFormSet(FormSet): AuthorFormset = formset_factory(AuthorFormSelection, can_delete=True, formset=AuthorFormSet) AuthorFormset.form_label = _("Authors") -AuthorFormset.form_admin_name = _(u"Authors") +AuthorFormset.form_admin_name = _("Authors") AuthorFormset.form_slug = "authors" class SearchQueryForm(forms.Form): - query = forms.CharField(max_length=None, label=_(u"Query"), initial='*', + query = forms.CharField(max_length=None, label=_("Query"), initial='*', widget=forms.HiddenInput) search_query = forms.ChoiceField(label="", required=False, choices=[]) label = forms.CharField(label="", max_length=None, required=False) - is_alert = forms.BooleanField(label=_(u"Is an alert"), required=False) + is_alert = forms.BooleanField(label=_("Is an alert"), required=False) create_or_update = forms.ChoiceField( - choices=(('create', _(u"Create")), - ('update', _(u"Update"))), initial='create') + choices=(('create', _("Create")), + ('update', _("Update"))), initial='create') def __init__(self, profile, content_type, *args, **kwargs): self.profile = profile @@ -1691,17 +1690,17 @@ class SearchQueryForm(forms.Form): def clean(self): data = self.cleaned_data if data['create_or_update'] == 'create' and not data['label']: - raise forms.ValidationError(_(u"A label is required for a new " - u"search query.")) + raise forms.ValidationError(_("A label is required for a new " + "search query.")) elif data['create_or_update'] == 'update': if not data['search_query']: - raise forms.ValidationError(_(u"Select the search query to " - u"update")) + raise forms.ValidationError(_("Select the search query to " + "update")) q = models.SearchQuery.objects.filter( profile=self.profile, content_type=self.content_type, pk=data['search_query']) if not q.count(): - raise forms.ValidationError(_(u"Query does not exist.")) + raise forms.ValidationError(_("Query does not exist.")) return data def save(self): @@ -1716,14 +1715,14 @@ class SearchQueryForm(forms.Form): profile=self.profile, content_type=self.content_type, pk=data['search_query']) except models.SearchQuery.DoesNotExist: - raise forms.ValidationError(_(u"Query does not exist.")) + raise forms.ValidationError(_("Query does not exist.")) sq.query = data['query'] sq.save() return sq class QRSearchForm(forms.Form): - query = forms.CharField(max_length=None, label=_(u"Query"), initial='*') + query = forms.CharField(max_length=None, label=_("Query"), initial='*') current_url = forms.CharField(max_length=None, label="", widget=forms.HiddenInput()) diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 38e5b2fb4..071b05598 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -4284,8 +4284,8 @@ class Merge(models.Model): for m in self.merge_exclusion.all(): m.delete() - def merge(self, item): - merge_model_objects(self, item) + def merge(self, item, keep_old=False): + merge_model_objects(self, item, keep_old=keep_old) self.generate_merge_candidate() diff --git a/ishtar_common/views.py b/ishtar_common/views.py index b662c1a4d..2176c4519 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -948,7 +948,7 @@ def reset_wizards(request): ITEM_PER_PAGE = 20 -def merge_action(model, form, key): +def merge_action(model, form, key, name_key="name"): def merge(request, page=1): current_url = key + '_merge' if not page: @@ -974,7 +974,8 @@ def merge_action(model, form, key): item_nb_1 = item_nb + ITEM_PER_PAGE from_key = 'from_' + key to_key = 'to_' + key - queryset = q.all().order_by(from_key + '__name')[item_nb:item_nb_1] + queryset = q.all().order_by( + from_key + '__' + name_key)[item_nb:item_nb_1] FormSet.from_key = from_key FormSet.to_key = to_key if request.method == 'POST': @@ -1016,7 +1017,7 @@ organization_merge = merge_action( class IshtarMixin(object): - page_name = u"" + page_name = "" def get_context_data(self, **kwargs): context = super(IshtarMixin, self).get_context_data(**kwargs) |