#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2010-2012 É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. """ Operations forms definitions """ import datetime from django import forms from django.conf import settings from django.core import validators from django.core.exceptions import ObjectDoesNotExist from django.db.models import Max from django.forms.formsets import DELETION_FIELD_NAME from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.translation import ugettext_lazy as _ from ishtar_common.models import valid_id, PersonType, Person, Town from archaeological_files.models import File import models from ishtar_common import widgets from ishtar_common.forms import FinalForm, FormSet, ClosingDateFormSelection, \ formset_factory, get_now, reverse_lazy, get_form_selection from ishtar_common.forms_common import TownForm, TownFormSet, TownFormset, \ AuthorFormset, SourceForm, SourceSelect, \ SourceDeletionForm, get_town_field class ParcelForm(forms.Form): form_label = _("Parcels") base_model = 'parcel' associated_models = {'parcel':models.Parcel, 'town':models.Town} town = forms.ChoiceField(label=_("Town"), choices=(), required=False, validators=[valid_id(models.Town)]) section = forms.CharField(label=_(u"Section"), required=False, validators=[validators.MaxLengthValidator(4)]) parcel_number = forms.CharField(label=_(u"Parcel number"), required=False, validators=[validators.MaxLengthValidator(6)]) year = forms.IntegerField(label=_("Year"), required=False, validators=[validators.MinValueValidator(1900), validators.MaxValueValidator(2100)]) def __init__(self, *args, **kwargs): towns = None if 'data' in kwargs and 'TOWNS' in kwargs['data']: towns = kwargs['data']['TOWNS'] # clean data if not "real" data prefix_value = kwargs['prefix'] + '-town' if not [k for k in kwargs['data'].keys() if k.startswith(prefix_value) and kwargs['data'][k]]: kwargs['data'] = None if 'files' in kwargs: kwargs.pop('files') super(ParcelForm, self).__init__(*args, **kwargs) if towns: self.fields['town'].choices = [('', '--')] + towns def clean(self): """Check required fields""" if any(self.errors): return if not self.cleaned_data or DELETION_FIELD_NAME in self.cleaned_data \ and self.cleaned_data[DELETION_FIELD_NAME]: return for key in ('town', 'parcel_number', 'section'): if not key in self.cleaned_data or not self.cleaned_data[key]: raise forms.ValidationError(_(u"Town section and parcel number " u"fields are required.")) return self.cleaned_data class ParcelFormSet(FormSet): def clean(self): """Checks that no parcels are duplicated.""" return self.check_duplicate(('town', 'parcel_number', 'year'), _(u"There are identical parcels.")) ParcelFormSet = formset_factory(ParcelForm, can_delete=True, formset=ParcelFormSet) ParcelFormSet.form_label = _(u"Parcels") class OperationSelect(forms.Form): common_name = forms.CharField(label=_(u"Name"), max_length=30) towns = get_town_field() operation_type = forms.ChoiceField(label=_(u"Operation type"), choices=[]) remains = forms.ChoiceField(label=_(u"Remains"), choices=models.RemainType.get_types()) year = forms.IntegerField(label=_("Year")) end_date = forms.NullBooleanField(label=_(u"Is open?")) def __init__(self, *args, **kwargs): super(OperationSelect, self).__init__(*args, **kwargs) self.fields['operation_type'].choices = models.OperationType.get_types() self.fields['operation_type'].help_text = models.OperationType.get_help() class OperationFormSelection(forms.Form): form_label = _(u"Operation search") associated_models = {'pk':models.Operation} currents = {'pk':models.Operation} pk = forms.IntegerField(label="", required=False, widget=widgets.JQueryJqGrid(reverse_lazy('get-operation'), OperationSelect(), models.Operation, source_full=reverse_lazy('get-operation-full')), validators=[valid_id(models.Operation)]) 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 select an operation.")) return cleaned_data class OperationCodeInput(forms.TextInput): """Manage auto complete whene changing year in form""" def render(self, *args, **kwargs): name, value = args base_name = '-'.join(name.split('-')[:-1]) rendered = super(OperationCodeInput, self).render(*args, **kwargs) js = u"""\n \n""" % {'base_name':base_name, 'name':name, 'url':reverse_lazy('get_available_operation_code')} return rendered + js class OperationFormGeneral(forms.Form): form_label = _(u"General") associated_models = {'in_charge':Person, 'associated_file':File, 'operation_type':models.OperationType} currents = {'associated_file':File} pk = forms.IntegerField(required=False, widget=forms.HiddenInput) in_charge = forms.IntegerField(label=_("Person in charge of the operation"), widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-person', args=["_".join( [unicode(PersonType.objects.get(txt_idx='head_scientist').pk), unicode(PersonType.objects.get(txt_idx='sra_agent').pk)])]), associated_model=Person, new=True), validators=[valid_id(Person)], required=False) associated_file = forms.IntegerField(label=_(u"Archaelogical file"), widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-file'), associated_model=File), validators=[valid_id(File)], required=False) operation_type = forms.ChoiceField(label=_(u"Operation type"), choices=[]) start_date = forms.DateField(label=_(u"Start date"), required=False, widget=widgets.JQueryDate) excavation_end_date = forms.DateField(label=_(u"Excavation end date"), required=False, widget=widgets.JQueryDate) surface = forms.IntegerField(required=False, widget=widgets.AreaWidget, label=_(u"Total surface (m²)"), validators=[validators.MinValueValidator(0), validators.MaxValueValidator(999999999)]) year = forms.IntegerField(label=_(u"Year"), initial=lambda:datetime.datetime.now().year, validators=[validators.MinValueValidator(1900), validators.MaxValueValidator(2100)]) operation_code = forms.IntegerField(label=_(u"Operation code"), initial=models.Operation.get_available_operation_code, widget=OperationCodeInput) common_name = forms.CharField(label=_(u"Generic name"), required=False, max_length=120, widget=forms.Textarea) operator_reference = forms.CharField(label=_(u"Operator reference"), required=False, max_length=20) if settings.COUNTRY == 'fr': code_patriarche = forms.IntegerField(label=u"Code PATRIARCHE", required=False) code_dracar = forms.CharField(label=u"Code DRACAR", required=False, validators=[validators.MaxLengthValidator(10)]) comment = forms.CharField(label=_(u"Comment"), widget=forms.Textarea, required=False) def __init__(self, *args, **kwargs): super(OperationFormGeneral, self).__init__(*args, **kwargs) self.fields['operation_type'].choices = models.OperationType.get_types() self.fields['operation_type'].help_text = models.OperationType.get_help() def clean(self): cleaned_data = self.cleaned_data # verify the logic between start date and excavation end date if cleaned_data['excavation_end_date']: if not self.cleaned_data['start_date']: raise forms.ValidationError(_(u"If you want to set an " u"excavation end date you have to provide a start date.")) if cleaned_data['excavation_end_date'] < cleaned_data['start_date']: raise forms.ValidationError(_(u"The excavation end date "\ u"cannot be before the start date.")) year = self.cleaned_data.get("year") operation_code = cleaned_data.get("operation_code") ops = models.Operation.objects.filter(year=year, operation_code=operation_code) # manage unique operation ID if 'pk' in cleaned_data and cleaned_data['pk']: ops = ops.exclude(pk=cleaned_data['pk']) if ops.count(): max_val = models.Operation.objects.filter(year=year).aggregate( Max('operation_code'))["operation_code__max"] raise forms.ValidationError(_(u"Operation code already exist for " u"year: %(year)d - use a value bigger than %(last_val)d") % { 'year':year, 'last_val':max_val}) return self.cleaned_data class OperationFormPreventive(forms.Form): form_label = _(u"Preventive informations - excavation") cost = forms.IntegerField(label=_(u"Cost (€)"), required=False) scheduled_man_days = forms.IntegerField(label=_(u"Scheduled man-days"), required=False) optional_man_days = forms.IntegerField(label=_(u"Optional man-days"), required=False) effective_man_days = forms.IntegerField(label=_(u"Effective man-days"), required=False) if settings.COUNTRY == 'fr': fnap_financing = forms.FloatField(required=False, label=u"Pourcentage de financement FNAP", validators=[validators.MinValueValidator(0), validators.MaxValueValidator(100)]) class OperationFormPreventiveDiag(forms.Form): form_label = _("Preventive informations - diagnostic") if settings.COUNTRY == 'fr': zoning_prescription = forms.NullBooleanField(required=False, label=_(u"Prescription on zoning")) large_area_prescription = forms.NullBooleanField(required=False, label=_(u"Prescription on large area")) geoarchaeological_context_prescription = forms.NullBooleanField( required=False, label=_(u"Prescription on geoarchaeological context")) class SelectedTownForm(forms.Form): form_label = _("Towns") associated_models = {'town':Town} town = forms.ChoiceField(label=_("Town"), choices=(), validators=[valid_id(Town)]) def __init__(self, *args, **kwargs): towns = None if 'data' in kwargs and 'TOWNS' in kwargs['data']: towns = kwargs['data']['TOWNS'] # clean data if not "real" data prefix_value = kwargs['prefix'] + '-town' if not [k for k in kwargs['data'].keys() if k.startswith(prefix_value) and kwargs['data'][k]]: kwargs['data'] = None if 'files' in kwargs: kwargs.pop('files') super(SelectedTownForm, self).__init__(*args, **kwargs) if towns and towns != -1: self.fields['town'].choices = [('', '--')] + towns SelectedTownFormset = formset_factory(SelectedTownForm, can_delete=True, formset=TownFormSet) SelectedTownFormset.form_label = _(u"Towns") class SelectedParcelForm(forms.Form): form_label = _("Parcels") associated_models = {'parcel':models.Parcel} parcel = forms.ChoiceField(label=_("Parcel"), choices=(), validators=[valid_id(models.Parcel)]) def __init__(self, *args, **kwargs): parcels = None if 'data' in kwargs and 'PARCELS' in kwargs['data']: parcels = kwargs['data']['PARCELS'] # clean data if not "real" data prefix_value = kwargs['prefix'] + '-parcel' if not [k for k in kwargs['data'].keys() if k.startswith(prefix_value) and kwargs['data'][k]]: kwargs['data'] = None if 'files' in kwargs: kwargs.pop('files') super(SelectedParcelForm, self).__init__(*args, **kwargs) if parcels: self.fields['parcel'].choices = [('', '--')] + parcels SelectedParcelFormSet = formset_factory(SelectedParcelForm, can_delete=True, formset=ParcelFormSet) SelectedParcelFormSet.form_label = _("Parcels") SelectedParcelGeneralFormSet = formset_factory(ParcelForm, can_delete=True, formset=ParcelFormSet) SelectedParcelGeneralFormSet.form_label = _("Parcels") class RemainForm(forms.Form): form_label = _("Remain types") base_model = 'remain' associated_models = {'remain':models.RemainType} remain = forms.ChoiceField(label=_("Remain type"), required=False, choices=models.RemainType.get_types()) class RemainFormSet(FormSet): def clean(self): """Checks that no remain types are duplicated.""" return self.check_duplicate(['remain_type'], _(u"There are identical remain types")) RemainFormset = formset_factory(RemainForm, can_delete=True, formset=RemainFormSet) RemainFormset.form_label = _("Remain types") class PeriodForm(forms.Form): form_label = _("Periods") base_model = 'period' associated_models = {'period':models.Period} period = forms.ChoiceField(label=_("Period"), required=False, choices=models.Period.get_types()) class PeriodFormSet(FormSet): def clean(self): """Checks that no period are duplicated.""" return self.check_duplicate(['period'], _(u"There are identical periods")) PeriodFormset = formset_factory(PeriodForm, can_delete=True, formset=PeriodFormSet) PeriodFormset.form_label = _("Periods") class FinalOperationClosingForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to close this operation?") class OperationDeletionForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to delete this operation?") #################################### # Source management for operations # #################################### class OperationSourceForm(SourceForm): pk = forms.IntegerField(required=False, widget=forms.HiddenInput) index = forms.IntegerField(label=_(u"Index")) hidden_operation_id = forms.IntegerField(label="", widget=forms.HiddenInput) def __init__(self, *args, **kwargs): super(OperationSourceForm, self).__init__(*args, **kwargs) keyOrder = self.fields.keyOrder keyOrder.pop(keyOrder.index('index')) keyOrder.insert(keyOrder.index('source_type') + 1, 'index') def clean(self): # manage unique operation ID cleaned_data = self.cleaned_data operation_id = cleaned_data.get("hidden_operation_id") index = cleaned_data.get("index") srcs = models.OperationSource.objects.filter(index=index, operation__pk=operation_id) if 'pk' in cleaned_data and cleaned_data['pk']: srcs = srcs.exclude(pk=cleaned_data['pk']) if srcs.count(): max_val = models.OperationSource.objects.filter( operation__pk=operation_id ).aggregate(Max('index'))["index__max"] operation = models.Operation.objects.get(pk=operation_id) raise forms.ValidationError(_(u"Index already exist for " "operation: %(operation)s - use a value bigger than %(last_val)d") % { "operation":unicode(operation), 'last_val':max_val}) return cleaned_data SourceOperationFormSelection = get_form_selection( 'SourceOperationFormSelection', _(u"Operation search"), 'operation', models.Operation, OperationSelect, 'get-operation', _(u"You should select an operation.")) class OperationSourceSelect(SourceSelect): operation__towns = get_town_field(label=_(u"Operation's town")) operation__operation_type = forms.ChoiceField(label=_(u"Operation type"), choices=[]) operation__year = forms.IntegerField(label=_(u"Operation's year")) def __init__(self, *args, **kwargs): super(OperationSourceSelect, self).__init__(*args, **kwargs) self.fields['operation__operation_type'].choices = \ models.OperationType.get_types() self.fields['operation__operation_type'].help_text = \ models.OperationType.get_help() OperationSourceFormSelection = get_form_selection( 'OperationSourceFormSelection', _(u"Documentation search"), 'pk', models.OperationSource, OperationSourceSelect, 'get-operationsource', _(u"You should select a document.")) ################################################ # Administrative act management for operations # ################################################ class AdministrativeActOpeSelect(forms.Form): operation__towns = get_town_field() act_type = forms.ChoiceField(label=_("Act type"), choices=[]) def __init__(self, *args, **kwargs): super(AdministrativeActOpeSelect, self).__init__(*args, **kwargs) self.fields['act_type'].choices = models.ActType.get_types( dct={'intented_to':'O'}) self.fields['act_type'].help_text = models.ActType.get_help( dct={'intented_to':'O'}) class AdministrativeActOpeFormSelection(forms.Form): form_label = _("Administrative act search") associated_models = {'pk':models.AdministrativeAct} currents = {'pk':models.AdministrativeAct} pk = forms.IntegerField(label="", required=False, widget=widgets.JQueryJqGrid(reverse_lazy('get-administrativeactop'), AdministrativeActOpeSelect(), models.AdministrativeAct, table_cols='TABLE_COLS_OPE'), validators=[valid_id(models.AdministrativeAct)]) 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 select an administrative" " act.")) return cleaned_data class AdministrativeActOpeForm(forms.Form): form_label = _("General") associated_models = {'act_type':models.ActType, 'signatory':Person} act_type = forms.ChoiceField(label=_("Act type"), choices=[]) signatory = forms.IntegerField(label=_("Signatory"), widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-person'), associated_model=Person, new=True), validators=[valid_id(Person)]) act_object = forms.CharField(label=_(u"Object"), max_length=200, widget=forms.Textarea) signature_date = forms.DateField(label=_(u"Signature date"), widget=widgets.JQueryDate) if settings.COUNTRY == 'fr': ref_sra = forms.CharField(label=u"Référence SRA", max_length=15) def __init__(self, *args, **kwargs): super(AdministrativeActOpeForm, self).__init__(*args, **kwargs) self.fields['act_type'].choices = models.ActType.get_types( dct={'intented_to':'O'}) self.fields['act_type'].help_text = models.ActType.get_help( dct={'intented_to':'O'}) class FinalAdministrativeActDeleteForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to delete this administrative act?")