diff options
Diffstat (limited to 'archaeological_operations')
| -rw-r--r-- | archaeological_operations/forms.py | 166 | ||||
| -rw-r--r-- | archaeological_operations/migrations/0013_operation_images.py | 21 | ||||
| -rw-r--r-- | archaeological_operations/models.py | 4 | ||||
| -rw-r--r-- | archaeological_operations/tests.py | 105 | ||||
| -rw-r--r-- | archaeological_operations/views.py | 5 | ||||
| -rw-r--r-- | archaeological_operations/wizards.py | 6 | 
6 files changed, 238 insertions, 69 deletions
| diff --git a/archaeological_operations/forms.py b/archaeological_operations/forms.py index 841131da6..6966fff50 100644 --- a/archaeological_operations/forms.py +++ b/archaeological_operations/forms.py @@ -49,7 +49,7 @@ from ishtar_common import widgets  from ishtar_common.forms import FinalForm, FormSet, get_now, \      reverse_lazy, get_form_selection, TableSelect, get_data_from_formset, \ -    ManageOldType +    ManageOldType, CustomForm, FieldType  from ishtar_common.forms_common import TownFormSet, SourceForm, SourceSelect, \      get_town_field @@ -477,6 +477,8 @@ class RecordRelationsFormSetBase(FormSet):  RecordRelationsFormSet = formset_factory(      RecordRelationsForm, can_delete=True, formset=RecordRelationsFormSetBase)  RecordRelationsFormSet.form_label = _(u"Relations") +RecordRelationsFormSet.form_admin_name = _("Operations - Relations") +RecordRelationsFormSet.form_slug = "operation-relations"  class OperationSelect(TableSelect): @@ -675,12 +677,15 @@ class OperationFormFileChoice(forms.Form):          validators=[valid_id(File)], required=False) -class OperationFormAbstract(forms.Form): +class OperationFormAbstract(CustomForm, forms.Form):      form_label = _(u"Abstract") +    form_admin_name = _("Operations - Abstract") +    form_slug = "operation-abstract"      abstract = forms.CharField(          label=_(u"Abstract"),          widget=forms.Textarea(attrs={'class': 'xlarge'}), required=False) +  SLICING = (("month", _(u"months")), ('year', _(u"years")),)  DATE_SOURCE = (('creation', _(u"Creation date")), @@ -761,8 +766,11 @@ class DashboardForm(forms.Form):          return fltr -class OperationFormGeneral(ManageOldType, forms.Form): +class OperationFormGeneral(ManageOldType, CustomForm, forms.Form):      form_label = _(u"General") +    form_admin_name = _(u"Operation - General") +    form_slug = "operation-general" +      file_upload = True      associated_models = {'scientist': Person,                           'in_charge': Person, @@ -882,33 +890,36 @@ class OperationFormGeneral(ManageOldType, forms.Form):                  'height': settings.IMAGE_MAX_SIZE[1]}),          max_length=255, required=False, widget=widgets.ImageFileInput()) +    FILE_FIELDS = [ +        'report_delivery_date', +        'report_processing', +        'cira_rapporteur', +        'cira_date', +        'negative_result' +    ] +    WAREHOUSE_FIELDS = [ +        'documentation_deadline', +        'documentation_received', +        'finds_deadline', +        'finds_received', +    ] +    TYPES = [ +        FieldType('operation_type', models.OperationType), +        FieldType('report_processing', models.ReportState), +    ] +      def __init__(self, *args, **kwargs):          super(OperationFormGeneral, self).__init__(*args, **kwargs)          profile = get_current_profile()          if not profile.files: -            self.fields.pop('report_delivery_date') -            self.fields.pop('report_processing') -            self.fields.pop('cira_rapporteur') -            self.fields.pop('cira_date') -            self.fields.pop('negative_result') +            for key in self.FILE_FIELDS: +                self.remove_field(key)              if not profile.warehouse: -                self.fields.pop('documentation_deadline') -                self.fields.pop('documentation_received') -                self.fields.pop('finds_deadline') -                self.fields.pop('finds_received') -        self.fields['operation_type'].choices = \ -            models.OperationType.get_types( -                initial=self.init_data.get('operation_type')) -        self.fields['operation_type'].help_text = \ -            models.OperationType.get_help() -        if 'report_processing' in self.fields: -            self.fields['report_processing'].choices = \ -                models.ReportState.get_types( -                    initial=self.init_data.get('report_processing')) -            self.fields['report_processing'].help_text = \ -                models.ReportState.get_help() -        self.fields['record_quality'].choices = \ -            [('', '--')] + list(models.QUALITY) +                for key in self.WAREHOUSE_FIELDS: +                    self.remove_field(key) +        if 'record_quality' in self.fields: +            self.fields['record_quality'].choices = \ +                [('', '--')] + list(models.QUALITY)          if 'operation_code' in self.fields:              fields = OrderedDict()              ope_code = self.fields.pop('operation_code') @@ -920,17 +931,20 @@ class OperationFormGeneral(ManageOldType, forms.Form):      def clean(self):          cleaned_data = self.cleaned_data +          # verify the logic between start date and excavation end date -        if cleaned_data.get('excavation_end_date'): +        if self.are_available(['excavation_end_date', 'start_date']) \ +                and cleaned_data.get('excavation_end_date'):              if not self.cleaned_data['start_date']:                  raise forms.ValidationError( -                    _(u"If you want to set an excavation end date you have to " -                      u"provide a start date.")) +                    _(u"If you want to set an excavation end date you " +                      u"have to provide a start date."))              if cleaned_data['excavation_end_date'] \                      < cleaned_data['start_date']:                  raise forms.ValidationError(                      _(u"The excavation end date cannot be before the start "                        u"date.")) +          # verify patriarche          code_p = self.cleaned_data.get('code_patriarche', None) @@ -942,11 +956,13 @@ class OperationFormGeneral(ManageOldType, forms.Form):                  msg = u"Ce code OA a déjà été affecté à une "\                        u"autre opération"                  raise forms.ValidationError(msg) +          # manage unique operation ID          year = self.cleaned_data.get("year")          operation_code = cleaned_data.get("operation_code", None)          if not operation_code:              return self.cleaned_data +          ops = models.Operation.objects.filter(year=year,                                                operation_code=operation_code)          if 'pk' in cleaned_data and cleaned_data['pk']: @@ -989,14 +1005,18 @@ class OperationFormModifGeneral(OperationFormGeneral):              fields[key] = value          self.fields = fields +  OperationFormModifGeneral.associated_models = \      OperationFormGeneral.associated_models.copy()  OperationFormModifGeneral.associated_models['associated_file'] = File -class CollaboratorForm(forms.Form): +class CollaboratorForm(CustomForm, forms.Form):      form_label = _(u"Collaborators") +    form_admin_name = _(u"Operation - Collaborators") +    form_slug = "operation-collaborators" +      base_models = ['collaborator']      associated_models = {'collaborator': Person, }      collaborator = widgets.Select2MultipleField( @@ -1004,11 +1024,15 @@ class CollaboratorForm(forms.Form):      def __init__(self, *args, **kwargs):          super(CollaboratorForm, self).__init__(*args, **kwargs) -        self.fields['collaborator'].widget.attrs['full-width'] = True +        if 'collaborator' in self.fields: +            self.fields['collaborator'].widget.attrs['full-width'] = True -class OperationFormPreventive(forms.Form): +class OperationFormPreventive(CustomForm, forms.Form):      form_label = _(u"Preventive informations - excavation") +    form_admin_name = _(u"Operation - Preventive - Excavation") +    form_slug = "operation-preventive-excavation" +      cost = forms.IntegerField(label=_(u"Cost (euros)"), required=False)      scheduled_man_days = forms.IntegerField(label=_(u"Scheduled man-days"),                                              required=False) @@ -1023,8 +1047,11 @@ class OperationFormPreventive(forms.Form):                          validators.MaxValueValidator(100)]) -class OperationFormPreventiveDiag(forms.Form): +class OperationFormPreventiveDiag(CustomForm, forms.Form):      form_label = _("Preventive informations - diagnostic") +    form_admin_name = _(u"Operation - Preventive - Diagnostic") +    form_slug = "operation-preventive-diagnostic" +      if settings.COUNTRY == 'fr':          zoning_prescription = forms.NullBooleanField(              required=False, label=_(u"Prescription on zoning")) @@ -1049,9 +1076,11 @@ class SelectedTownForm(forms.Form):          if towns and towns != -1:              self.fields['town'].choices = [('', '--')] + towns +  SelectedTownFormset = formset_factory(SelectedTownForm, can_delete=True,                                        formset=TownFormSet)  SelectedTownFormset.form_label = _(u"Towns") +SelectedTownFormset.form_slug = "towns"  class SelectedParcelForm(forms.Form): @@ -1068,13 +1097,17 @@ class SelectedParcelForm(forms.Form):          if parcels:              self.fields['parcel'].choices = [('', '--')] + parcels +  SelectedParcelFormSet = formset_factory(SelectedParcelForm, can_delete=True,                                          formset=ParcelFormSet)  SelectedParcelFormSet.form_label = _("Parcels") +SelectedParcelFormSet.form_admin_name = _(u"Operations - Parcels") +SelectedParcelFormSet.form_slug = "operation-parcels"  SelectedParcelGeneralFormSet = formset_factory(ParcelForm, can_delete=True,                                                 formset=ParcelFormSet) -SelectedParcelGeneralFormSet.form_label = _("Parcels") +SelectedParcelGeneralFormSet.form_admin_name = _("Parcels") +SelectedParcelGeneralFormSet.form_slug = "operation-parcels"  """  class SelectedParcelFormSet(forms.Form): @@ -1102,36 +1135,36 @@ class SelectedParcelFormSet(forms.Form):  """ -class RemainForm(ManageOldType, forms.Form): +class RemainForm(CustomForm, ManageOldType, forms.Form):      form_label = _("Remain types") +    form_admin_name = _("Operations - Remains") +    form_slug = "operation-remains" +      base_model = 'remain'      associated_models = {'remain': models.RemainType}      remain = forms.MultipleChoiceField(          label=_("Remain type"), required=False, choices=[],          widget=forms.CheckboxSelectMultiple) -    def __init__(self, *args, **kwargs): -        super(RemainForm, self).__init__(*args, **kwargs) -        self.fields['remain'].choices = models.RemainType.get_types( -            initial=self.init_data.get('remain'), -            empty_first=False) -        self.fields['remain'].help_text = models.RemainType.get_help() +    TYPES = [ +        FieldType('remain', models.RemainType, True), +    ] -class PeriodForm(ManageOldType, forms.Form): +class PeriodForm(CustomForm, ManageOldType, forms.Form):      form_label = _("Periods") +    form_admin_name = _("Operations - Periods") +    form_slug = "operation-periods" +      base_model = 'period'      associated_models = {'period': models.Period}      period = forms.MultipleChoiceField(          label=_("Period"), required=False, choices=[],          widget=forms.CheckboxSelectMultiple) -    def __init__(self, *args, **kwargs): -        super(PeriodForm, self).__init__(*args, **kwargs) -        self.fields['period'].choices = models.Period.get_types( -            initial=self.init_data.get('period'), -            empty_first=False) -        self.fields['period'].help_text = models.Period.get_help() +    TYPES = [ +        FieldType('period', models.Period, True), +    ]  class ArchaeologicalSiteForm(ManageOldType, forms.Form): @@ -1144,21 +1177,16 @@ class ArchaeologicalSiteForm(ManageOldType, forms.Form):          label=_("Remains"), choices=[], widget=widgets.Select2Multiple,          required=False) +    TYPES = [ +        FieldType('periods', models.Period, True), +        FieldType('remains', models.RemainType, True), +    ] +      def __init__(self, *args, **kwargs):          self.limits = {}          if 'limits' in kwargs:              kwargs.pop('limits')          super(ArchaeologicalSiteForm, self).__init__(*args, **kwargs) -        self.fields['periods'].choices = \ -            models.Period.get_types( -                empty_first=False, -                initial=self.init_data.get('periods')) -        self.fields['periods'].help_text = models.Period.get_help() -        self.fields['remains'].choices = \ -            models.RemainType.get_types( -                initial=self.init_data.get('remains'), -                empty_first=False) -        self.fields['remains'].help_text = models.RemainType.get_help()      def clean_reference(self):          reference = self.cleaned_data['reference'] @@ -1197,6 +1225,9 @@ class ArchaeologicalSiteBasicForm(forms.Form):  ArchaeologicalSiteFormSet = formset_factory(ArchaeologicalSiteBasicForm,                                              can_delete=True, formset=FormSet)  ArchaeologicalSiteFormSet.form_label = _("Archaeological sites") +ArchaeologicalSiteFormSet.form_admin_name = _("Operation - Archaeological " +                                              "sites") +ArchaeologicalSiteFormSet.form_slug = "operation-archaeological-sites"  class ArchaeologicalSiteSelectionForm(forms.Form): @@ -1224,6 +1255,9 @@ class OperationDeletionForm(FinalForm):  class OperationSourceForm(SourceForm): +    form_admin_name = _("Operation Sources - Main") +    form_slug = "operation-source-relations" +      pk = forms.IntegerField(required=False, widget=forms.HiddenInput)      index = forms.IntegerField(label=_(u"Index"))      hidden_operation_id = forms.IntegerField(label="", @@ -1371,8 +1405,11 @@ class AdministrativeActOpeFormSelection(forms.Form):          return cleaned_data -class AdministrativeActOpeForm(ManageOldType, forms.Form): +class AdministrativeActOpeForm(CustomForm, ManageOldType, forms.Form):      form_label = _("General") +    form_admin_name = _("Operations - Administrative act - General") +    form_slug = "operation-adminact-general" +      associated_models = {'act_type': models.ActType, }      #                     'signatory':Person}      act_type = forms.ChoiceField(label=_("Act type"), choices=[]) @@ -1388,13 +1425,10 @@ class AdministrativeActOpeForm(ManageOldType, forms.Form):          ref_sra = forms.CharField(label=u"Autre référence", max_length=15,                                    required=False) -    def __init__(self, *args, **kwargs): -        super(AdministrativeActOpeForm, self).__init__(*args, **kwargs) -        self.fields['act_type'].choices = models.ActType.get_types( -            initial=self.init_data.get('act_type'), -            dct={'intented_to': 'O'}) -        self.fields['act_type'].help_text = models.ActType.get_help( -            dct={'intented_to': 'O'}) +    TYPES = [ +        FieldType('act_type', models.ActType, +                  extra_args={"dct": {'intented_to': 'O'}}), +    ]  class AdministrativeActModifForm(object): @@ -1410,7 +1444,7 @@ class AdministrativeActModifForm(object):      def clean(self):          # manage unique act ID -        year = self.cleaned_data.get("signature_date") +        year = self.cleaned_data.get("signature_date", None)          if not year or not hasattr(year, 'year'):              return self.cleaned_data          year = year.year diff --git a/archaeological_operations/migrations/0013_operation_images.py b/archaeological_operations/migrations/0013_operation_images.py new file mode 100644 index 000000000..e32d9371f --- /dev/null +++ b/archaeological_operations/migrations/0013_operation_images.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-11-10 17:17 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('ishtar_common', '0021_auto_20171110_1717'), +        ('archaeological_operations', '0012_auto_20171026_1827'), +    ] + +    operations = [ +        migrations.AddField( +            model_name='operation', +            name='images', +            field=models.ManyToManyField(blank=True, to='ishtar_common.IshtarImage', verbose_name='Images'), +        ), +    ] diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index cf648a43a..2f1ea3ea3 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -38,7 +38,7 @@ from ishtar_common.models import GeneralType, BaseHistorizedItem, \      SourceType, Person, Organization, Town, Dashboard, IshtarUser, ValueGetter,\      DocumentTemplate, ShortMenuItem, DashboardFormItem, GeneralRelationType,\      GeneralRecordRelations, post_delete_record_relation, OperationType, \ -    ImageModel, post_save_cache, PersonType +    ImageModel, post_save_cache, PersonType, IshtarImage  class RemainType(GeneralType): @@ -352,6 +352,8 @@ class Operation(ClosedItem, BaseHistorizedItem, ImageModel, OwnPerms,      comment = models.TextField(_(u"General comment"), null=True, blank=True)      scientific_documentation_comment = models.TextField(          _(u"Comment about scientific documentation"), null=True, blank=True) +    images = models.ManyToManyField(IshtarImage, verbose_name=_(u"Images"), +                                    blank=True)      cached_label = models.CharField(_(u"Cached name"), max_length=500,                                      null=True, blank=True, db_index=True)      archaeological_sites = models.ManyToManyField( diff --git a/archaeological_operations/tests.py b/archaeological_operations/tests.py index ec7ae44c5..af6199774 100644 --- a/archaeological_operations/tests.py +++ b/archaeological_operations/tests.py @@ -40,7 +40,7 @@ from ishtar_common.models import OrganizationType, Organization, ItemKey, \      ImporterType, IshtarUser, TargetKey, ImporterModel, IshtarSiteProfile, \      Town, ImporterColumn, Person, Author, SourceType, AuthorType, \      DocumentTemplate, PersonType, TargetKeyGroup, JsonDataField, \ -    JsonDataSection, ImportTarget, FormaterType +    JsonDataSection, ImportTarget, FormaterType, CustomForm, ExcludedField  from archaeological_files.models import File, FileType  from archaeological_context_records.models import Unit @@ -1142,6 +1142,109 @@ class OperationTest(TestCase, OperationInitTest):          self.assertNotIn(u"Marmotte".encode('utf-8'), response.content) +class CustomFormTest(TestCase, OperationInitTest): +    fixtures = FILE_FIXTURES + +    def setUp(self): +        IshtarSiteProfile.objects.get_or_create( +            slug='default', active=True) +        self.username, self.password, self.user = create_superuser() +        self.alt_username, self.alt_password, self.alt_user = create_user() +        self.alt_user.user_permissions.add(Permission.objects.get( +            codename='view_own_operation')) +        self.orgas = self.create_orgas(self.user) +        self.operations = self.create_operation(self.user, self.orgas[0]) +        self.operations += self.create_operation(self.alt_user, self.orgas[0]) +        self.item = self.operations[0] + +    def test_filters(self): +        c = Client() +        c.login(username=self.username, password=self.password) + +        cls_wiz = OperationWizardModifTest +        url = reverse(cls_wiz.url_name) +        # first wizard step +        step = 'selec-operation_modification' +        cls_wiz.wizard_post(c, url, step, {'pk': self.operations[0].pk}) + +        step = 'general-operation_modification' +        data = { +            '{}{}-current_step'.format(cls_wiz.url_name, +                                       cls_wiz.wizard_name): [step], +        } +        key_in_charge = "in_charge" +        response = c.post(url, data) +        self.assertIn( +            key_in_charge, response.content, +            msg="filter all - 'in charge' field not found on the modification " +                "wizard") +        f = CustomForm.objects.create(name="Test", form="operation-general", +                                      available=True, apply_to_all=True) +        ExcludedField.objects.create(custom_form=f, field="in_charge") + +        response = c.post(url, data) +        self.assertNotIn( +            key_in_charge, response.content, +            msg="filter all - 'in charge' field found on the modification " +                "wizard. It should have been filtered.") + +        # user type form prevail on "all" +        f_scientist = CustomForm.objects.create( +            name="Test", form="operation-general", available=True) +        tpe = PersonType.objects.get(txt_idx='head_scientist') +        key_address = "address" +        f_scientist.user_types.add(tpe) +        self.user.ishtaruser.person.person_types.add(tpe) +        ExcludedField.objects.create(custom_form=f_scientist, field="address") +        response = c.post(url, data) +        self.assertIn( +            key_in_charge, response.content, +            msg="filter user type - 'in charge' field not found on the " +                "modification wizard. It should not have been filtered.") +        self.assertNotIn( +            key_address, response.content, +            msg="filter user type - 'address' field found on the " +                "modification wizard. It should have been filtered.") + +        # user prevail on "all" and "user_types" +        f_user = CustomForm.objects.create( +            name="Test", form="operation-general", available=True) +        f_user.users.add(self.user.ishtaruser) +        self.user.ishtaruser.person.person_types.add(tpe) +        response = c.post(url, data) +        self.assertIn( +            key_in_charge, response.content, +            msg="filter user - 'in charge' field not found on the modification " +                "wizard. It should not have been filtered.") +        self.assertIn( +            key_address, response.content, +            msg="filter user - 'address' field not found on the modification " +                "wizard. It should not have been filtered.") + +    def test_enabled(self): +        c = Client() +        c.login(username=self.username, password=self.password) + +        cls_wiz = OperationWizardModifTest +        url = reverse(cls_wiz.url_name) +        # first wizard step +        step = 'selec-operation_modification' +        cls_wiz.wizard_post(c, url, step, {'pk': self.operations[0].pk}) + +        step = 'collaborators-operation_modification' +        data = { +            '{}{}-current_step'.format(cls_wiz.url_name, +                                       cls_wiz.wizard_name): [step], +        } +        response = c.post(url, data) +        self.assertNotEqual(response.status_code, 404) +        CustomForm.objects.create( +            name="Test2", form="operation-collaborators", available=True, +            apply_to_all=True, enabled=False) +        response = c.post(url, data) +        self.assertEqual(response.status_code, 404) + +  class OperationSearchTest(TestCase, OperationInitTest):      fixtures = FILE_FIXTURES diff --git a/archaeological_operations/views.py b/archaeological_operations/views.py index 98da31801..f295e0f9d 100644 --- a/archaeological_operations/views.py +++ b/archaeological_operations/views.py @@ -82,6 +82,7 @@ def autocomplete_archaeologicalsite(request):                         for site in sites])      return HttpResponse(data, content_type='text/plain') +  new_archaeologicalsite = new_item(models.ArchaeologicalSite,                                    ArchaeologicalSiteForm, many=True) @@ -132,6 +133,7 @@ def get_available_operation_code(request, year=None):                         models.Operation.get_available_operation_code(year)})      return HttpResponse(data, content_type='text/plain') +  get_operation = get_item(models.Operation, 'get_operation', 'operation')  show_operation = show_item(models.Operation, 'operation') @@ -158,11 +160,13 @@ def dashboard_operation(request, *args, **kwargs):      dct = {'dashboard': models.OperationDashboard()}      return render(request, 'ishtar/dashboards/dashboard_operation.html', dct) +  operation_search_wizard = SearchWizard.as_view(      [('general-operation_search', OperationFormSelection)],      label=_(u"Operation search"),      url_name='operation_search',) +  wizard_steps = [      ('filechoice-operation_creation', OperationFormFileChoice),      ('general-operation_creation', OperationFormGeneral), @@ -191,6 +195,7 @@ def get_check_files_for_operation(other_check=None):          return other_check(self)      return func +  check_files_for_operation = get_check_files_for_operation() diff --git a/archaeological_operations/wizards.py b/archaeological_operations/wizards.py index fffe34ca7..24c1af45b 100644 --- a/archaeological_operations/wizards.py +++ b/archaeological_operations/wizards.py @@ -23,6 +23,7 @@ from django.conf import settings  from django.core.exceptions import ObjectDoesNotExist  from django.core.urlresolvers import reverse  from django.db.models import Max +from django.http import Http404  from django.shortcuts import render  from django.utils.translation import ugettext_lazy as _ @@ -149,7 +150,10 @@ class OperationWizard(Wizard):              data = {}          if not step:              step = self.steps.current -        form = self.get_form_list()[step] +        try: +            form = self.get_form_list()[step] +        except KeyError: +            raise Http404()          # manage the dynamic choice of towns          if step.startswith('towns') and hasattr(form, 'management_form'):              data['TOWNS'] = self.get_towns() | 
