#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2010-2016 Étienne Loks # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # See the file COPYING for details. """ Context records forms definitions """ from itertools import groupby from django import forms from django.conf import settings from django.core import validators from django.forms.formsets import formset_factory from django.utils.translation import ugettext_lazy as _ import models from archaeological_operations.forms import OperationSelect, ParcelField, \ RecordRelationsForm as OpeRecordRelationsForm, RecordRelationsFormSetBase from archaeological_operations.models import Period, Parcel, Operation, \ ArchaeologicalSite, RelationType as OpeRelationType from archaeological_operations.widgets import OAWidget from bootstrap_datepicker.widgets import DatePicker from ishtar_common import widgets from ishtar_common.forms import FinalForm, FormSet, \ reverse_lazy, get_form_selection, TableSelect, ManageOldType, CustomForm, \ FieldType from ishtar_common.forms_common import get_town_field from ishtar_common.models import valid_id, IshtarSiteProfile, Town class OperationFormSelection(CustomForm, forms.Form): form_label = _("Operation") form_admin_name = _(u"Context record - 010 - Operation choice") form_slug = "contextrecord-010-operationchoice" associated_models = {'operation': Operation} currents = {'operation': Operation} operation = forms.IntegerField( label=_(u"Operation"), required=False, widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-operation'), associated_model=Operation), validators=[valid_id(Operation)]) class RecordSelect(TableSelect): search_vector = forms.CharField(label=_(u"Full text search"), widget=widgets.SearchWidget) label = forms.CharField(label=_(u"ID"), max_length=100) town = get_town_field() if settings.COUNTRY == 'fr': operation__code_patriarche = forms.CharField( max_length=500, widget=OAWidget, label=_(u"Code PATRIARCHE")) operation__year = forms.IntegerField(label=_(u"Operation's year")) operation__operation_code = forms.IntegerField( label=_(u"Operation's number (index by year)")) archaeological_site = forms.IntegerField( label=_("Archaeological site"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-archaeologicalsite'), associated_model=ArchaeologicalSite), validators=[valid_id(ArchaeologicalSite)]) ope_relation_types = forms.MultipleChoiceField( label=_(u"Search within related operations"), choices=[], widget=forms.CheckboxSelectMultiple) datings__period = forms.ChoiceField(label=_(u"Period"), choices=[]) unit = forms.ChoiceField(label=_(u"Unit type"), choices=[]) parcel = ParcelField(label=_(u"Parcel (section/number/public domain)")) cr_relation_types = forms.MultipleChoiceField( label=_(u"Search within relations"), choices=[], widget=forms.CheckboxSelectMultiple) def __init__(self, *args, **kwargs): super(RecordSelect, self).__init__(*args, **kwargs) self.fields['datings__period'].choices = Period.get_types() self.fields['datings__period'].help_text = Period.get_help() self.fields['unit'].choices = models.Unit.get_types() self.fields['unit'].help_text = models.Unit.get_help() self.fields['cr_relation_types'].choices = \ models.RelationType.get_types(empty_first=False) self.fields['ope_relation_types'].choices = OpeRelationType.get_types( empty_first=False) def get_input_ids(self): ids = super(RecordSelect, self).get_input_ids() ids.pop(ids.index('parcel')) ids.append('parcel_0') ids.append('parcel_1') ids.append('parcel_2') ids.pop(ids.index('cr_relation_types')) for idx, c in enumerate(self.fields['cr_relation_types'].choices): ids.append('cr_relation_types_{}'.format(idx)) ids.pop(ids.index('ope_relation_types')) for idx, c in enumerate(self.fields['ope_relation_types'].choices): ids.append('ope_relation_types_{}'.format(idx)) return ids class RecordFormSelection(forms.Form): form_label = _("Context record search") SEARCH_AND_SELECT = True associated_models = {'pk': models.ContextRecord} currents = {'pk': models.ContextRecord} pk = forms.IntegerField( label="", required=False, widget=widgets.DataTable( reverse_lazy('get-contextrecord'), RecordSelect, models.ContextRecord, source_full=reverse_lazy('get-contextrecord-full')), validators=[valid_id(models.ContextRecord)]) def clean(self): cleaned_data = self.cleaned_data if 'pk' not in cleaned_data or not cleaned_data['pk']: raise forms.ValidationError(_(u"You should at least select one " u"context record.")) return cleaned_data class RecordFormGeneral(CustomForm, ManageOldType): form_label = _("General") form_admin_name = _(u"Context record - 020 - General") form_slug = "contextrecord-020-general" file_upload = True base_models = ["documentation"] associated_models = { 'archaeological_site': ArchaeologicalSite, 'parcel': Parcel, 'unit': models.Unit, 'town': Town, 'documentation': models.DocumentationType, 'excavation_technic': models.ExcavationTechnicType} pk = forms.IntegerField(required=False, widget=forms.HiddenInput) operation_id = forms.IntegerField(widget=forms.HiddenInput) parcel = forms.ChoiceField(label=_(u"Parcel"), choices=[]) town = forms.ChoiceField(label=_(u"Town"), choices=[], required=False) archaeological_site = forms.ChoiceField( label=" ", choices=[], required=False, help_text=_(u"Only the items associated to the operation can be " u"selected.") ) label = forms.CharField(label=_(u"ID"), validators=[validators.MaxLengthValidator(200)]) description = forms.CharField(label=_(u"Description"), widget=forms.Textarea, required=False) comment = forms.CharField(label=_(u"General comment"), widget=forms.Textarea, required=False) excavation_technic = forms.ChoiceField( label=_(u"Excavation technique"), choices=[], required=False) length = forms.FloatField(label=_(u"Length (m)"), required=False) width = forms.FloatField(label=_(u"Width (m)"), required=False) thickness = forms.FloatField(label=_(u"Thickness (m)"), required=False) diameter = forms.FloatField(label=_(u"Diameter (m)"), required=False) depth = forms.FloatField(label=_(u"Depth (m)"), required=False) depth_of_appearance = forms.FloatField( label=_(u"Depth of appearance (m)"), required=False) unit = forms.ChoiceField(label=_(u"Context record type"), required=False, choices=[]) opening_date = forms.DateField(label=_(u"Opening date"), widget=DatePicker, required=False) closing_date = forms.DateField(label=_(u"Closing date"), widget=DatePicker, required=False) documentation = forms.MultipleChoiceField( label=_("Documentation"), choices=[], required=False, widget=forms.CheckboxSelectMultiple) location = forms.CharField( label=_(u"Location"), widget=forms.Textarea, required=False, validators=[validators.MaxLengthValidator(200)]) TYPES = [ FieldType('unit', models.Unit), FieldType('excavation_technic', models.ExcavationTechnicType), FieldType('documentation', models.DocumentationType, is_multiple=True) ] def __init__(self, *args, **kwargs): # TODO: simplify operation = None if 'data' in kwargs and kwargs['data'] and \ ('operation' in kwargs['data'] or 'context_record' in kwargs['data']): if 'operation' in kwargs['data']: operation = kwargs['data'].pop('operation') if type(operation) in (list, tuple): operation = operation[0] if not hasattr(operation, 'id'): operation_id = operation try: operation = Operation.objects.get(pk=operation_id) except Operation.DoesNotExist: operation = None # force operation modification # if posted if operation and kwargs['data']: if hasattr(operation, 'id'): kwargs['data'][kwargs['prefix'] + '-operation_id'] = \ operation.id else: kwargs['data'][kwargs['prefix'] + '-operation_id'] = \ operation if 'context_record' in kwargs['data'] and \ kwargs['data']['context_record']: operation = kwargs['data']['context_record'].operation # clean data if not "real" data # prefix_value = kwargs['prefix'] if not [k for k in kwargs['data'].keys() if k.startswith(kwargs['prefix']) and kwargs['data'][k]]: kwargs['data'] = None if 'files' in kwargs: kwargs.pop('files') super(RecordFormGeneral, self).__init__(*args, **kwargs) profile = IshtarSiteProfile.get_current_profile() self.fields['parcel'].choices = [('', '--')] if not profile.parcel_mandatory: self.fields['parcel'].required = False self.fields['town'].choices = [('', '--')] else: self.fields.pop('town') site_label = IshtarSiteProfile.get_default_site_label() self.fields['archaeological_site'].label = site_label self.fields['archaeological_site'].choices = [('', '--')] if operation: self.fields['operation_id'].initial = operation.pk parcels = operation.parcels.all() sort = lambda x: (x.town.name, x.section) parcels = sorted(parcels, key=sort) for key, gparcels in groupby(parcels, sort): self.fields['parcel'].choices.append( (" - ".join([k for k in key if k]), [(parcel.pk, parcel.short_label) for parcel in gparcels]) ) if len(self.fields['parcel'].choices) == 1: # only the empty choice is available self.fields.pop('parcel') if 'town' in self.fields: self.fields['town'].required = True if 'town' in self.fields: if self.fields['town'].required: self.fields['town'].choices = [] # remove the empty choice self.fields['town'].choices += [(t.pk, unicode(t)) for t in operation.towns.all()] self.fields['archaeological_site'].choices += [ (site.pk, unicode(site)) for site in operation.archaeological_sites.all() ] def clean(self): # manage unique context record ID cleaned_data = self.cleaned_data operation_id = cleaned_data.get("operation_id") label = cleaned_data.get("label") cr = models.ContextRecord.objects.filter( label=label, parcel__operation__pk=operation_id) if 'pk' in cleaned_data and cleaned_data['pk']: cr = cr.exclude(pk=int(cleaned_data['pk'])) if cr.count(): raise forms.ValidationError(_(u"This ID already exists for " u"this operation.")) if not self.cleaned_data.get('parcel', None) and not \ self.cleaned_data.get('town', None): raise forms.ValidationError(_(u"You have to choose a town or a " u"parcel.")) return cleaned_data class DatingForm(ManageOldType, forms.Form): form_label = _("Dating") base_model = 'dating' associated_models = {'dating_type': models.DatingType, 'quality': models.DatingQuality, 'period': models.Period} period = forms.ChoiceField(label=_("Period"), choices=[]) start_date = forms.IntegerField(label=_(u"Start date"), required=False) end_date = forms.IntegerField(label=_(u"End date"), required=False) quality = forms.ChoiceField(label=_("Quality"), required=False, choices=[]) dating_type = forms.ChoiceField(label=_("Dating type"), required=False, choices=[]) TYPES = [ FieldType('dating_type', models.DatingType), FieldType('quality', models.DatingQuality), FieldType('period', models.Period) ] DatingFormSet = formset_factory(DatingForm, can_delete=True, formset=FormSet) DatingFormSet.form_label = _("Dating") DatingFormSet.form_admin_name = _(u"Context record - 030 - Dating") DatingFormSet.form_slug = "contextrecord-030-datings" class RecordRelationsForm(OpeRecordRelationsForm): current_model = models.RelationType current_related_model = models.ContextRecord associated_models = {'right_record': models.ContextRecord, 'relation_type': models.RelationType} right_record = forms.ChoiceField( label=_(u"Context record"), choices=[], required=False) def __init__(self, *args, **kwargs): crs = None if 'data' in kwargs and 'CONTEXT_RECORDS' in kwargs['data']: crs = kwargs['data']['CONTEXT_RECORDS'] super(RecordRelationsForm, self).__init__(*args, **kwargs) self.fields['relation_type'].choices = \ models.RelationType.get_types( initial=self.init_data.get('relation_type')) if crs: self.fields['right_record'].choices = [('', '-' * 2)] + crs RecordRelationsFormSet = formset_factory( RecordRelationsForm, can_delete=True, formset=RecordRelationsFormSetBase) RecordRelationsFormSet.form_label = _(u"Relations") RecordRelationsFormSet.form_admin_name = _(u"Context record - 050 - Relations") RecordRelationsFormSet.form_slug = "contextrecord-050-recordrelations" class RecordFormInterpretation(CustomForm, ManageOldType, forms.Form): form_label = _("Interpretation") form_admin_name = "Context record - 040 - Interpretation" form_slug = "contextrecord-040-interpretation" associated_models = {'activity': models.ActivityType, 'identification': models.IdentificationType} datings_comment = forms.CharField( label=_(u"Comments on dating"), required=False, widget=forms.Textarea) filling = forms.CharField(label=_(u"Filling"), widget=forms.Textarea, required=False) interpretation = forms.CharField(label=_(u"Interpretation"), widget=forms.Textarea, required=False) activity = forms.ChoiceField(label=_(u"Activity"), required=False, choices=[]) identification = forms.ChoiceField(label=_("Identification"), required=False, choices=[]) taq = forms.IntegerField(label=_(u"TAQ"), required=False) taq_estimated = forms.IntegerField(label=_(u"Estimated TAQ"), required=False) tpq = forms.IntegerField(label=_(u"TPQ"), required=False) tpq_estimated = forms.IntegerField(label=_(u"Estimated TPQ"), required=False) TYPES = [ FieldType('activity', models.ActivityType), FieldType('identification', models.IdentificationType), ] OperationRecordFormSelection = get_form_selection( 'OperationRecordFormSelection', _(u"Operation search"), 'operation_id', Operation, OperationSelect, 'get-operation', _(u"You should select an operation.")) class RecordDeletionForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to delete this context record?")