#/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2010-2011 É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. """ Forms definition """ import datetime import re from itertools import groupby from django.core.urlresolvers import reverse from django.core import validators from django.core.exceptions import ObjectDoesNotExist from django.utils import formats from django.utils.functional import lazy from django.utils.translation import ugettext_lazy as _ from django.shortcuts import render_to_response from django.template import Context, RequestContext, loader from django.db.models import Max from django import forms from django.core.mail import send_mail from django.forms.formsets import formset_factory, BaseFormSet, \ DELETION_FIELD_NAME from django.contrib.auth.models import User from django.contrib.sites.models import Site from formwizard.forms import NamedUrlSessionFormWizard import models import widgets from ishtar import settings reverse_lazy = lazy(reverse, unicode) def clean_duplicated(formset, key_names): """Checks for duplicated.""" if any(formset.errors): return items = [] for i in range(0, formset.total_form_count()): form = formset.forms[i] if not form.is_valid(): continue item = [key_name in form.cleaned_data and form.cleaned_data[key_name] for key_name in key_names] if not [v for v in item if v]: continue if item in items: raise forms.ValidationError, \ _("There are identical items.") items.append(item) regexp_name = re.compile(r'^[\w\- ]+$', re.UNICODE) name_validator = validators.RegexValidator(regexp_name, _(u"Enter a valid name consisting of letters, spaces and hyphens."), 'invalid') class WarehouseForm(forms.Form): name = forms.CharField(label=_(u"Name"), max_length=40, validators=[name_validator]) warehouse_type = forms.ChoiceField(label=_(u"Warehouse type"), choices=models.WarehouseType.get_types()) person_in_charge = forms.IntegerField(label=_(u"Person in charge"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-person'), associated_model=models.Person), validators=[models.valid_id(models.Person)], required=False) comment = forms.CharField(label=_(u"Comment"), widget=forms.Textarea, required=False) address = forms.CharField(label=_(u"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, required=False) town = forms.CharField(label=_(u"Town"), max_length=30, required=False) country = forms.CharField(label=_(u"Country"), max_length=30, required=False) phone = forms.CharField(label=_(u"Phone"), max_length=18, required=False) mobile_phone = forms.CharField(label=_(u"Town"), max_length=18, required=False) def save(self, user): dct = self.cleaned_data dct['history_modifier'] = user dct['warehouse_type'] = models.WarehouseType.objects.get( pk=dct['warehouse_type']) if 'person_in_charge' in dct and dct['person_in_charge']: dct['person_in_charge'] = models.Person.objects.get( pk=dct['person_in_charge']) new_item = models.Warehouse(**dct) new_item.save() return new_item class FinalForm(forms.Form): final = True form_label = _("Confirm") class FormSet(BaseFormSet): def add_fields(self, form, index): super(FormSet, self).add_fields(form, index) form.fields[DELETION_FIELD_NAME].label = '' form.fields[DELETION_FIELD_NAME].widget = widgets.DeleteWidget() class SearchWizard(NamedUrlSessionFormWizard): model = None def get_wizard_name(self): """ As the class name can interfere when reused, use the url_name """ return self.url_name def get_template(self, request, storage): templates = ['search.html'] return templates class Wizard(NamedUrlSessionFormWizard): model = None def get_wizard_name(self): """ As the class name can interfere when reused, use the url_name """ return self.url_name def get_template(self, request, storage): templates = ['default_wizard.html'] current_step = storage.get_current_step() or self.get_first_step( request, storage) if current_step == self.get_last_step(request, storage): templates = ['confirm_wizard.html'] + templates return templates def get_template_context(self, request, storage, form=None): """ Add previous and current steps to manage the wizard path """ context = super(Wizard, self).get_template_context(request, storage, form) step = self.get_first_step(request, storage) current_step = storage.get_current_step() or self.get_first_step( request, storage) context.update({'current_step':self.form_list[current_step]}) if step == current_step: return context previous_steps = [] while step: if step == current_step: break previous_steps.append(self.form_list[step]) step = self.get_next_step(request, storage, step) context.update({'previous_steps':previous_steps}) # not last step: validation if step != self.get_last_step(request, storage): return context final_form_list = [] for form_key in self.get_form_list(request, storage).keys(): form_obj = self.get_form(request, storage, step=form_key, data=storage.get_step_data(form_key), files=storage.get_step_files(form_key)) form_obj.is_valid() final_form_list.append(form_obj) last_form = final_form_list[-1] context.update({'datas':self.get_formated_datas(final_form_list)}) if hasattr(last_form, 'confirm_msg'): context.update({'confirm_msg':last_form.confirm_msg}) if hasattr(last_form, 'confirm_end_msg'): context.update({'confirm_end_msg':last_form.confirm_end_msg}) return context def get_formated_datas(self, forms): """ Get the data to present in the last page """ datas = [] for form in forms: form_datas = [] base_form = hasattr(form, 'forms') and form.forms[0] or form associated_models = hasattr(base_form, 'associated_models') and \ base_form.associated_models or {} if not hasattr(form, 'cleaned_data') and hasattr(form, 'forms'): cleaned_datas = [frm.cleaned_data for frm in form.forms if frm.is_valid()] if not cleaned_datas: continue elif not hasattr(form, 'cleaned_data'): continue else: cleaned_datas = type(form.cleaned_data) == list and \ form.cleaned_data \ or [form.cleaned_data] for cleaned_data in cleaned_datas: if not cleaned_data: continue if form_datas: form_datas.append(("", "", "spacer")) items = hasattr(base_form, 'fields') and \ base_form.fields.keyOrder or cleaned_data.keys() for key in items: lbl = None if key.startswith('hidden_'): continue if hasattr(base_form, 'fields') and key in base_form.fields: lbl = base_form.fields[key].label if not lbl: continue value = cleaned_data[key] if not value and value != False: continue if type(value) == bool: if value == True: value = _("Yes") elif value == False: value = _("No") elif key in associated_models: item = associated_models[key].objects.get(pk=value) if hasattr(item, 'short_label'): value = item.short_label() else: value = unicode(item) form_datas.append((lbl, value, '')) if form_datas: datas.append((form.form_label, form_datas)) return datas def get_extra_model(self, dct, request, storage, form_list): dct['history_modifier'] = request.user return dct def done(self, request, storage, form_list, return_object=False, **kwargs): """ Save to the model """ dct, m2m = {}, [] for form in form_list: if not form.is_valid(): return self.render(request, storage, form) base_form = hasattr(form, 'forms') and form.forms[0] or form associated_models = hasattr(base_form, 'associated_models') and \ base_form.associated_models or {} if hasattr(form, 'forms'): multi = False if form.forms: frm = form.forms[0] fields = frm.fields.copy() if 'DELETE' in fields: fields.pop('DELETE') multi = len(fields) > 1 if multi: assert hasattr(frm, 'base_model'), \ u"Must define a base_model for " + unicode(frm.__class__) for frm in form.forms: if not frm.is_valid(): continue vals = {} if "DELETE" in frm.cleaned_data: if frm.cleaned_data["DELETE"]: continue frm.cleaned_data.pop('DELETE') for key in frm.cleaned_data: value = frm.cleaned_data[key] if not value and value != False: continue if key in associated_models: value = associated_models[key].objects.get(pk=value) if multi: vals[key] = value else: m2m.append((key, value)) if multi and vals: m2m.append((frm.base_model, vals)) elif type(form.cleaned_data) == dict: for key in form.cleaned_data: if key.startswith('hidden_'): continue value = form.cleaned_data[key] if key in associated_models: if value: value = associated_models[key].objects.get(pk=value) else: value = None dct[key] = value return self.save_model(dct, m2m, request, storage, form_list, return_object) def get_saved_model(self): """ Permit a distinguo when saved model is not the base selected model """ return self.model def get_current_saved_object(self, request, storage): """ Permit a distinguo when saved model is not the base selected model """ return self.get_current_object(request, storage) def save_model(self, dct, m2m, request, storage, form_list, return_object): dct = self.get_extra_model(dct, request, storage, form_list) obj = self.get_current_saved_object(request, storage) # manage dependant items other_objs = {} for k in dct.keys(): if '__' not in k: continue vals = k.split('__') assert len(vals) == 2, "Only one level of dependant item is managed" dependant_item, key = vals if dependant_item not in other_objs: other_objs[dependant_item] = {} other_objs[dependant_item][key] = dct.pop(k) if obj: for k in dct: if k.startswith('pk'): continue setattr(obj, k, dct[k]) for dependant_item in other_objs: c_item = getattr(obj, dependant_item) if c_item: # to check # for k in other_objs[dependant_item]: setattr(c_item, k, other_objs[dependant_item][k]) c_item.save() else: m = getattr(self.model, dependant_item) if hasattr(m, 'related'): c_item = m.related.model(**other_objs[dependant_item]) setattr(obj, dependant_item, c_item) obj.save() obj.save() else: adds = {} for dependant_item in other_objs: m = getattr(self.model, dependant_item) model = m.field.rel.to c_dct = other_objs[dependant_item].copy() if hasattr(model, 'history'): c_dct['history_modifier'] = request.user c_item = model(**c_dct) c_item.save() if hasattr(m, 'through'): adds[dependant_item] = c_item elif hasattr(m, 'field'): dct[dependant_item] = c_item if 'pk' in dct: dct.pop('pk') obj = self.get_saved_model()(**dct) obj.save() for k in adds: getattr(obj, k).add(adds[k]) m2m_items = {} for key, value in m2m: if key not in m2m_items: getattr(obj, key+'s').clear() if type(key) == dict: vals = [] for item in getattr(obj, key+'s').all(): v = {} for k in value.keys(): v[k] = getattr(item, k) vals.append(v) m2m_items[key] = vals else: m2m_items[key] = getattr(obj, key+'s').all() if value not in m2m_items[key]: if type(value) == dict: value = getattr(obj, key+'s').model.objects.create(**value) value.save() getattr(obj, key+'s').add(value) obj.save() res = render_to_response('wizard_done.html', {}, context_instance=RequestContext(request)) return return_object and (obj, res) or res def get_form(self, request, storage, step=None, data=None, files=None): """ Manage formset """ if data: data = data.copy() if not step: step = self.determine_step(request, storage) form = self.get_form_list(request, storage)[step] if hasattr(form, 'management_form'): # manage deletion not_to_delete, to_delete = [], [] for key in data.keys(): items = key.split('-') if len(items) == 4: if items[2] not in to_delete and \ items[2] not in not_to_delete: del_key = "-".join(items[:3]+["DELETE"]) if del_key in data and data[del_key]: to_delete.append(items[2]) else: not_to_delete.append(items[2]) if items[2] in to_delete: data.pop(key) if to_delete: # reorganize for idx, number in enumerate(sorted(not_to_delete)): if unicode(idx) == number: continue for key in data.keys(): items = key.split('-') if len(items) == 3 and items[1] == number: ck = '-'.join([items[0], unicode(idx), items[2]]) data[ck] = data.pop(key)[0] # get a form key base_key = form.form.base_fields.keys()[0] init = self.get_form_initial(request, storage, step) total_field = len([key for key in data.keys() if base_key in key.split('-') and data[key]]) if init and not to_delete: total_field = max((total_field, len(init))) data[step + u'-INITIAL_FORMS'] = unicode(total_field) data[step + u'-TOTAL_FORMS'] = unicode(total_field + 1) data = data or None form = super(Wizard, self).get_form(request, storage, step, data, files) return form def render_next_step(self, request, storage, form, **kwargs): """ Manage the modify or delete button in formset: next_step = current_step """ if request.POST.has_key('formset_modify') \ and request.POST['formset_modify'] \ or [key for key in request.POST.keys() if key.endswith('DELETE') and request.POST[key]]: return self.render(request, storage, form, **kwargs) return super(Wizard, self).render_next_step(request, storage, form, **kwargs) def process_post_request(self, request, storage, *args, **kwargs): """ Convert numerical step number to step name """ if request.POST.has_key('form_prev_step'): try: step_number = int(request.POST['form_prev_step']) post_data = request.POST.copy() post_data['form_prev_step'] = self.get_form_list(request, storage).keys()[step_number] request.POST = post_data except ValueError: pass return super(Wizard, self).process_post_request(request, storage, *args, **kwargs) @classmethod def session_has_key(cls, request, storage, form_key, key=None, multi=None): """ Check if the session has value of a specific form and (if provided) of a key """ test = storage.prefix in request.session \ and 'step_data' in request.session[storage.prefix] \ and form_key in request.session[storage.prefix]['step_data'] if not key or not test: return test key = key.startswith(form_key) and key or \ not multi and form_key + '-' + key or \ form_key + '-0-' + key #only check if the first field is available return key in request.session[storage.prefix]['step_data'][form_key] @classmethod def session_get_value(cls, request, storage, form_key, key, multi=False): """ Get the value of a specific form """ if not cls.session_has_key(request, storage, form_key, key, multi): return if not multi: key = key.startswith(form_key) and key or form_key + '-' + key return request.session[storage.prefix]['step_data'][form_key][key] vals = [] for k in request.session[storage.prefix]['step_data'][form_key]: if k.startswith(form_key) and k.endswith(key) and \ request.session[storage.prefix]['step_data'][form_key][k]: vals.append(request.session[storage.prefix]['step_data']\ [form_key][k]) return vals def get_current_object(self, request, storage): """ Get the current object for an instancied wizard """ current_obj = None main_form_key = 'selec-' + self.url_name try: idx = int(self.session_get_value(request, storage, main_form_key, 'pk')) current_obj = self.model.objects.get(pk=idx) except(TypeError, ValueError, ObjectDoesNotExist): pass return current_obj def get_form_initial(self, request, storage, step): current_obj = self.get_current_object(request, storage) current_step = storage.get_current_step() or self.get_first_step( request, storage) if step.startswith('selec-') and step in self.form_list \ and 'pk' in self.form_list[step].associated_models: model_name = self.form_list[step].associated_models['pk' ].__name__.lower() if step == current_step: self.reset_wizard(request, storage) val = model_name in request.session and request.session[model_name] if val: return {'pk':val} elif current_obj: return self.get_instanced_init(current_obj, request, storage, step) current_form = self.form_list[current_step] if hasattr(current_form, 'currents'): initial = {} for key in current_form.currents: model_name = current_form.currents[key].__name__.lower() val = model_name in request.session and \ request.session[model_name] if val: initial[key] = val if initial: return initial return super(Wizard, self).get_form_initial(request, storage, step) def get_instanced_init(self, obj, request, storage, step): """ Get initial data from an init """ current_step = storage.get_current_step() or self.get_first_step( request, storage) c_form = self.form_list[current_step] # make the current object the default item for the session obj_name = obj.__class__.__name__.lower() # prefer a specialized name if available prefixes = storage.prefix.split('_') if len(prefixes) > 1 and prefixes[-2].startswith(obj_name): obj_name = prefixes[-2] request.session[obj_name] = unicode(obj.pk) initial = {} if hasattr(c_form, 'base_fields'): for base_field in c_form.base_fields.keys(): fields = base_field.split('__') value = obj for field in fields: if not hasattr(value, field) or \ getattr(value, field) == None: value = obj break value = getattr(value, field) if value == obj: continue if hasattr(value, 'pk'): value = value.pk if value in (True, False): initial[base_field] = value elif value != None: initial[base_field] = unicode(value) elif hasattr(c_form, 'management_form'): initial = [] key = current_step.split('-')[0] if not hasattr(obj, key): return initial for child_obj in getattr(obj, key).all(): vals = {} keys = c_form.form.base_fields.keys() if len(keys) == 1: # only one field: must be the id of the object vals[keys[0]] = unicode(child_obj.pk) else: for field in keys: if hasattr(child_obj, field): value = getattr(child_obj, field) if hasattr(value, 'pk'): value = value.pk if value != None: vals[field] = unicode(value) if vals: initial.append(vals) return initial def get_now(): format = formats.get_format('DATE_INPUT_FORMATS')[0] value = datetime.datetime.now().strftime(format) return value class PersonWizard(Wizard): model = models.Person class PersonFormSelection(forms.Form): form_label = _("Person") associated_models = {'pk':models.Person} currents = {'pk':models.Person} pk = forms.IntegerField(label=_("Person"), widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-person'), associated_model=models.Person), validators=[models.valid_id(models.Person)]) class PersonForm(forms.Form): form_label = _("Identity") associated_models = {'attached_to':models.Organization, 'person_type':models.PersonType} title = forms.ChoiceField(label=_("Title"), choices=models.Person.TYPE) surname = forms.CharField(label=_(u"Surname"), max_length=20, validators=[name_validator]) name = forms.CharField(label=_(u"Name"), max_length=30, validators=[name_validator]) email = forms.CharField(label=_(u"Email"), max_length=40, required=False, validators=[validators.validate_email]) person_type = forms.ChoiceField(label=_("Person type"), choices=models.PersonType.get_types()) attached_to = forms.IntegerField(label=_("Current organization"), widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-organization'), associated_model=models.Organization), validators=[models.valid_id(models.Organization)], required=False) is_author = forms.BooleanField(label=_(u"Is an author?"), required=False) in_charge_storage = forms.BooleanField(required=False, label=_(u"In charge of a storage?")) person_creation_wizard = PersonWizard([ ('identity-person_creation', PersonForm), ('final-person_creation', FinalForm)], url_name='person_creation',) person_modification_wizard = PersonWizard([ ('selec-person_modification', PersonFormSelection), ('identity-person_modification', PersonForm), ('final-person_modification', FinalForm)], url_name='person_modification',) class AccountWizard(Wizard): model = models.Person def get_formated_datas(self, forms): datas = super(AccountWizard, self).get_formated_datas(forms) for form in forms: if not hasattr(form, "cleaned_data"): continue for key in form.cleaned_data: if key == 'hidden_password' and form.cleaned_data[key]: datas[-1][1].append((_("New password"), "*"*8)) return datas def done(self, request, storage, form_list, **kwargs): ''' Save the account ''' dct = {} for form in form_list: if not form.is_valid(): return self.render(request, storage, form) associated_models = hasattr(form, 'associated_models') and \ form.associated_models or {} if type(form.cleaned_data) == dict: for key in form.cleaned_data: if key == 'pk': continue value = form.cleaned_data[key] if key in associated_models and value: value = associated_models[key].objects.get(pk=value) dct[key] = value person = self.get_current_object(request, storage) if not person: return self.render(request, storage, form) for key in dct.keys(): if key.startswith('hidden_password'): dct['password'] = dct.pop(key) try: account = models.IshtarUser.objects.get(person=person) account.username = dct['username'] account.email = dct['email'] except ObjectDoesNotExist: now = datetime.datetime.now() account = models.IshtarUser(person=person, username=dct['username'], email=dct['email'], first_name=person.surname, last_name=person.name, is_staff=False, is_active=True, is_superuser=False, last_login=now, date_joined=now) if dct['password']: account.set_password(dct['password']) account.save() if 'send_password' in dct and dct['send_password'] and \ settings.ADMINS: site = Site.objects.get_current() app_name = site and ("Ishtar - " + site.name) \ or "Ishtar" context = Context({'login':dct['username'], 'password':dct['password'], 'app_name':app_name, 'site': site and site.domain or "" }) t = loader.get_template('account_activation_email.txt') msg = t.render(context) subject = _(u"[%(app_name)s] Account creation/modification") % { "app_name":app_name} send_mail(subject, msg, settings.ADMINS[0][1], [dct['email']], fail_silently=True) res = render_to_response('wizard_done.html', {}, context_instance=RequestContext(request)) return res def get_form(self, request, storage, step=None, data=None, files=None): """ Display the "Send email" field if necessary """ form = super(AccountWizard, self).get_form(request, storage, step, data, files) if not hasattr(form, 'is_hidden'): return form if self.session_get_value(request, storage, 'account-account_management', 'hidden_password'): form.is_hidden = False return form class AccountForm(forms.Form): form_label = _("Account") associated_models = {'pk':models.Person} currents = {'pk':models.Person} pk = forms.IntegerField(widget=forms.HiddenInput, required=False) username = forms.CharField(label=_(u"Account"), max_length=30) email = forms.CharField(label=_(u"Email"), max_length=75, validators=[validators.validate_email]) hidden_password = forms.CharField(label=_(u"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, widget=forms.PasswordInput, required=False) def __init__(self, *args, **kwargs): if 'initial' in kwargs and 'pk' in kwargs['initial']: try: person = models.Person.objects.get(pk=kwargs['initial']['pk']) account = models.IshtarUser.objects.get(person=person) kwargs['initial'].update({'username':account.username, 'email':account.email}) except ObjectDoesNotExist: pass return super(AccountForm, self).__init__(*args, **kwargs) def clean(self): cleaned_data = self.cleaned_data 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.")) 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 \ password.")) return cleaned_data class FinalAccountForm(forms.Form): final = True form_label = _("Confirm") send_password = forms.BooleanField(label=_(u"Send the new password by " u"email?"), required=False) def __init__(self, *args, **kwargs): self.is_hidden = True return super(FinalAccountForm, self).__init__(*args, **kwargs) account_management_wizard = AccountWizard([ ('selec-account_management', PersonFormSelection), ('account-account_management', AccountForm), ('final-account_management', FinalAccountForm)], url_name='account_management',) class FileWizard(Wizard): model = models.File object_parcel_type = 'associated_file' def get_form(self, request, storage, step=None, data=None, files=None): """ Manage towns """ if data: data = data.copy() else: data = {} # manage the dynamic choice of towns if not step: step = self.determine_step(request, storage) form = self.get_form_list(request, storage)[step] town_form_key = 'towns-' + self.url_name if step.startswith('parcels-') and hasattr(form, 'management_form') \ and self.session_has_key(request, storage, town_form_key): towns = [] qdict = request.session[storage.prefix]['step_data'][town_form_key] for k in qdict.keys(): if k.endswith("town") and qdict[k]: try: town = models.Town.objects.get(pk=int(qdict[k])) towns.append((town.pk, unicode(town))) except (ObjectDoesNotExist, ValueError): pass data['TOWNS'] = sorted(towns, key=lambda x:x[1]) form = super(FileWizard, self).get_form(request, storage, step, data, files) return form def get_extra_model(self, dct, request, storage, form_list): dct = super(FileWizard, self).get_extra_model(dct, request, storage, form_list) current_ref = models.File.objects.filter(year=dct['year'] ).aggregate(Max('numeric_reference'))["numeric_reference__max"] dct['numeric_reference'] = current_ref and current_ref + 1 or 1 return dct def done(self, request, storage, form_list, **kwargs): ''' Save parcels ''' r = super(FileWizard, self).done(request, storage, form_list, return_object=True, **kwargs) if type(r) not in (list, tuple) or len(r) != 2: return r obj, res = r for form in form_list: if not hasattr(form, 'prefix') \ or not form.prefix.startswith('parcels-') \ or not hasattr(form, 'forms'): continue for frm in form.forms: if not frm.is_valid(): continue dct = frm.cleaned_data.copy() if 'parcel' in dct: try: parcel = models.Parcel.objects.get(pk=dct['parcel']) setattr(parcel, self.object_parcel_type, obj) parcel.save() except (ValueError, ObjectDoesNotExist): continue continue try: dct['town'] = models.Town.objects.get(pk=int(dct['town'])) except (ValueError, ObjectDoesNotExist): continue dct['associated_file'], dct['operation'] = None, None dct[self.object_parcel_type] = obj if 'DELETE' in dct: dct.pop('DELETE') parcel = models.Parcel.objects.filter(**dct).count() if not parcel: dct['history_modifier'] = request.user parcel = models.Parcel(**dct) parcel.save() return res class FileSelect(forms.Form): towns = forms.IntegerField(label=_(u"Town"), widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ 'autocomplete-town', associated_model=models.Town), validators=[models.valid_id(models.Town)]) file_type = forms.ChoiceField(label=_("File type"), choices=models.FileType.get_types()) saisine_type = forms.ChoiceField(label=_("Saisine type"), choices=models.SaisineType.get_types()) year = forms.IntegerField(label=_("Year")) class FileFormSelection(forms.Form): form_label = _("Archaeological file") associated_models = {'pk':models.File} currents = {'pk':models.File} pk = forms.IntegerField(label="", required=False, widget=widgets.JQueryJqGrid(reverse_lazy('get-file'), FileSelect(), models.File), validators=[models.valid_id(models.File)]) 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 a file.")) return cleaned_data class FileFormGeneral(forms.Form): form_label = _("General") associated_models = {'in_charge':models.Person, 'related_file':models.File, 'file_type':models.FileType} in_charge = forms.IntegerField(label=_("Person in charge"), widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-person', args=[models.PersonType.objects.get(txt_idx='sra_agent').pk]), associated_model=models.Person), validators=[models.valid_id(models.Person)]) year = forms.IntegerField(label=_("Year"), initial=lambda:datetime.datetime.now().year, validators=[validators.MinValueValidator(1900), validators.MaxValueValidator(2100)]) numeric_reference = forms.IntegerField(label=_("Numeric reference"), widget=forms.HiddenInput, required=False) internal_reference = forms.CharField(label=_(u"Internal reference"), max_length=60, validators=[models.is_unique(models.File, 'internal_reference')]) creation_date = forms.DateField(label=_(u"Creation date"), initial=get_now, widget=widgets.JQueryDate) file_type = forms.ChoiceField(label=_("File type"), choices=models.FileType.get_types()) related_file = forms.IntegerField(label=_("Related file"), required=False, widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-file'), associated_model=models.File), validators=[models.valid_id(models.File)]) comment = forms.CharField(label=_(u"Comment"), widget=forms.Textarea, required=False) class FileFormGeneralRO(FileFormGeneral): year = forms.IntegerField(label=_("Year"), widget=forms.TextInput(attrs={'readonly':True})) numeric_reference = forms.IntegerField(label=_("Numeric reference"), widget=forms.TextInput(attrs={'readonly':True})) internal_reference = forms.CharField(label=_(u"Internal reference"), widget=forms.TextInput(attrs={'readonly':True},)) class FileFormAddress(forms.Form): form_label = _("Address") associated_models = {'town':models.Town} total_surface = forms.IntegerField(required=False, widget=widgets.AreaWidget, label=_(u"Total surface"), validators=[validators.MinValueValidator(0), validators.MaxValueValidator(999999999)]) address = forms.CharField(label=_(u"Main address"), widget=forms.Textarea) address_complement = forms.CharField(label=_(u"Main address - complement"), required=False) postal_code = forms.CharField(label=_(u"Main address - postal code"), max_length=10) class TownForm(forms.Form): form_label = _("Towns") associated_models = {'town':models.Town} # !FIXME hard_link, reverse_lazy doen't seem to work with formsets town = forms.IntegerField(label=_(u"Town"), required=False, widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ 'autocomplete-town', associated_model=models.Town), validators=[models.valid_id(models.Town)]) class TownFormSet(FormSet): def clean(self): """Checks that no towns are duplicated.""" if any(self.errors): return towns = [] for i in range(0, self.total_form_count()): form = self.forms[i] if not form.is_valid(): continue if 'town' not in form.cleaned_data or not form.cleaned_data['town']: continue town = form.cleaned_data['town'] if town in towns: raise forms.ValidationError, _("There are identical towns.") towns.append(town) TownFormSet = formset_factory(TownForm, can_delete=True, formset=TownFormSet) TownFormSet.form_label = _("Towns") 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=[models.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, initial=lambda:datetime.datetime.now().year, 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', 'year'): if not key in self.cleaned_data or not self.cleaned_data[key]: raise forms.ValidationError(_(u"All fields are required")) return self.cleaned_data class ParcelFormSet(FormSet): def clean(self): """Checks that no parcels are duplicated.""" return clean_duplicated(self, ('town', 'parcel_number', 'year')) ParcelFormSet = formset_factory(ParcelForm, can_delete=True, formset=ParcelFormSet) ParcelFormSet.form_label = _("Parcels") class FileFormPreventive(forms.Form): form_label = _("Preventive informations") associated_models = {'general_contractor':models.Person, 'saisine_type':models.SaisineType, 'permit_type':models.PermitType, 'town_planning_service':models.Organization} general_contractor = forms.IntegerField(label=_(u"General contractor"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-person', args=[models.PersonType.objects.get(txt_idx='general_contractor').pk]), associated_model=models.Person), validators=[models.valid_id(models.Person)]) town_planning_service = forms.IntegerField(required=False, label=_(u"Town planning service"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-organization', args=[models.OrganizationType.objects.get(txt_idx='planning_service').pk]), associated_model=models.Organization), validators=[models.valid_id(models.Organization)]) permit_type = forms.ChoiceField(label=_("Permit type"), required=False, choices=models.PermitType.get_types()) permit_reference = forms.CharField(label=_(u"Permit reference"), required=False, validators=[validators.MaxLengthValidator(60)]) total_developed_surface = forms.IntegerField(widget=widgets.AreaWidget, label=_(u"Total developed surface"), required=False, validators=[validators.MinValueValidator(0), validators.MaxValueValidator(999999999)]) if settings.COUNTRY == 'fr': saisine_type = forms.ChoiceField(label=_("Saisine type"), choices=models.SaisineType.get_types()) reception_date = forms.DateField(label=_(u"Reception date"), initial=get_now, widget=widgets.JQueryDate) def is_preventive(form_name, model, type_key='file_type', key=''): def func(self, request, storage): if storage.prefix not in request.session or \ 'step_data' not in request.session[storage.prefix] or \ form_name not in request.session[storage.prefix]['step_data'] or\ form_name + '-' + type_key not in \ request.session[storage.prefix]['step_data'][form_name]: return False try: type = int(request.session[storage.prefix]['step_data']\ [form_name][form_name+'-'+type_key]) return model.is_preventive(type, key) except ValueError: return False return func file_search_wizard = SearchWizard([('general-file_search', FileFormSelection)], url_name='file_search',) file_creation_wizard = FileWizard([ ('general-file_creation', FileFormGeneral), ('address-file_creation', FileFormAddress), ('towns-file_creation', TownFormSet), ('parcels-file_creation', ParcelFormSet), ('preventive-file_creation', FileFormPreventive), ('final-file_creation', FinalForm)], condition_list={ 'preventive-file_creation':is_preventive('general-file_creation', models.FileType) }, url_name='file_creation',) file_modification_wizard = FileWizard([ ('selec-file_modification', FileFormSelection), ('general-file_modification', FileFormGeneralRO), ('adress-file_modification', FileFormAddress), ('towns-file_modification', TownFormSet), ('parcels-file_modification', ParcelFormSet), ('preventive-file_modification', FileFormPreventive), ('final-file_modification', FinalForm)], condition_list={ 'preventive-file_modification':is_preventive('general-file_modification', models.FileType) }, url_name='file_modification',) class DeletionWizard(Wizard): def get_formated_datas(self, forms): datas = super(DeletionWizard, self).get_formated_datas(forms) self.current_obj = None for form in forms: if not hasattr(form, "cleaned_data"): continue for key in form.cleaned_data: if key == 'pk': model = form.associated_models['pk'] self.current_obj = model.objects.get(pk=form.cleaned_data['pk']) if not self.current_obj: return datas res = {} for field in self.model._meta.fields + self.model._meta.many_to_many: if field.name not in self.fields: continue value = getattr(self.current_obj, field.name) if not value: continue if hasattr(value, 'all'): value = ", ".join([unicode(item) for item in value.all()]) if not value: continue else: value = unicode(value) res[field.name] = (field.verbose_name, value, '') if not datas and self.fields: datas = [['', []]] for field in self.fields: if field in res: datas[0][1].append(res[field]) return datas def done(self, request, storage, form_list, **kwargs): obj = self.get_current_object(request, storage) obj.delete() return render_to_response('wizard_done.html', {}, context_instance=RequestContext(request)) class FileClosingWizard(DeletionWizard): model = models.File fields = ['year', 'numeric_reference', 'internal_reference', 'file_type', 'in_charge', 'general_contractor', 'creation_date', 'reception_date', 'total_surface', 'total_developed_surface', 'address', 'address_complement', 'postal_code', 'comment'] if settings.COUNTRY == 'fr': fields += ['saisine_type', 'reference_number'] fields += ['towns'] def get_formated_datas(self, forms): datas = super(FileClosingWizard, self).get_formated_datas(forms) self.current_obj = None for form in forms: if not hasattr(form, "cleaned_data"): continue for key in form.cleaned_data: if key == 'pk': model = form.associated_models['pk'] self.current_obj = model.objects.get(pk=form.cleaned_data['pk']) if not self.current_obj: return datas res = {} for field in self.model._meta.fields + self.model._meta.many_to_many: if field.name not in self.fields: continue value = getattr(self.current_obj, field.name) if not value: continue if hasattr(value, 'all'): value = ", ".join([unicode(item) for item in value.all()]) if not value: continue else: value = unicode(value) res[field.name] = (field.verbose_name, value, '') if not datas and self.fields: datas = [['', []]] for field in self.fields: if field in res: datas[0][1].append(res[field]) return datas class FileDeletionWizard(FileClosingWizard): def get_formated_datas(self, forms): datas = super(FileDeletionWizard, self).get_formated_datas(forms) datas.append((_("Associated operations"), [])) for operation in models.Operation.objects.filter( associated_file=self.current_obj).all(): if operation.end_date: datas[-1][1].append(('', unicode(operation))) return datas def done(self, request, storage, form_list, **kwargs): obj = self.get_current_object(request, storage) for operation in models.Operation.objects.filter( associated_file=obj).all(): operation.delete() obj.delete() return render_to_response('wizard_done.html', {}, context_instance=RequestContext(request)) class FinalOperationDeleteForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to delete this archaelogical file ?") file_deletion_wizard = FileDeletionWizard([ ('selec-file_deletion', FileFormSelection), ('final-file_deletion', FinalOperationDeleteForm)], url_name='file_deletion',) """ file_closing_wizard = FileWizard([ ('selec-file_closing', FileFormSelection), ('date-operation_closing', OperationDateFormSelection), ('final-operation_closing', FinalOperationClosingForm)], url_name='operation_closing',) """ class FileAdministrativeActWizard(FileWizard): edit = False def get_extra_model(self, dct, request, storage, form_list): dct['history_modifier'] = request.user return dct def get_associated_item(self, request, storage, dct): return self.get_current_object(request, storage) def save(self, dct, m2m, request, storage, form_list, return_object): associated_item = self.get_associated_item(request, storage, dct) if not associated_item: return self.render(request, storage, form_list[-1]) if isinstance(associated_item, models.File): dct['associated_file'] = associated_item elif isinstance(associated_item, models.Operation): dct['operation'] = associated_item dct['history_modifier'] = request.user if 'pk' in dct: dct.pop('pk') if self.edit: admact = self.get_current_object(request, storage) for k in dct: if hasattr(admact, k): setattr(admact, k, dct[k]) else: admact = models.AdministrativeAct(**dct) admact.save() res = render_to_response('wizard_done.html', {}, context_instance=RequestContext(request)) return res class FileEditAdministrativeActWizard(FileAdministrativeActWizard): model = models.AdministrativeAct edit = True def get_associated_item(self, request, storage, dct): return self.get_current_object(request, storage).associated_file class AdministrativeActForm(forms.Form): form_label = _("General") associated_models = {'act_type':models.ActType, 'signatory':models.Person} act_type = forms.ChoiceField(label=_("Act type"), choices=models.ActType.get_types(dct={'intented_to':'F'})) signatory = forms.IntegerField(label=_("Signatory"), widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-person'), associated_model=models.Person), validators=[models.valid_id(models.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) class AdministrativeActFileSelect(forms.Form): associated_file__towns = forms.IntegerField(label=_(u"Town"), widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ 'autocomplete-town', associated_model=models.Town), validators=[models.valid_id(models.Town)]) act_type = forms.ChoiceField(label=_("Act type"), choices=models.ActType.get_types(dct={'intented_to':'F'})) class AdministrativeActFileFormSelection(forms.Form): form_label = _("Administrative Act") associated_models = {'pk':models.AdministrativeAct} currents = {'pk':models.AdministrativeAct} pk = forms.IntegerField(label="", required=False, widget=widgets.JQueryJqGrid(reverse_lazy('get-administrativeactfile'), AdministrativeActFileSelect(), models.AdministrativeAct, table_cols='TABLE_COLS_FILE'), validators=[models.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 AdministrativeActDeletionWizard(FileClosingWizard): model = models.AdministrativeAct fields = ['act_type', 'in_charge', 'operator', 'scientific', 'signatory', 'operation', 'associated_file', 'signature_date', 'act_object',] if settings.COUNTRY == 'fr': fields += ['ref_sra'] def done(self, request, storage, form_list, **kwargs): obj = self.get_current_object(request, storage) obj.delete() return render_to_response('wizard_done.html', {}, context_instance=RequestContext(request)) class FinalAdministrativeActDeleteForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to delete this administrative act?") file_administrativeactfile_wizard = FileAdministrativeActWizard([ ('selec-file_administrativeactfile', FileFormSelection), ('administrativeact-file_administrativeactfile', AdministrativeActForm), ('final-file_administrativeactfile', FinalForm)], url_name='file_administrativeactfile',) file_administrativeactfile_modification_wizard = FileEditAdministrativeActWizard([ ('selec-file_administrativeactfile_modification', AdministrativeActFileFormSelection), ('administrativeact-file_administrativeactfile_modification', AdministrativeActForm), ('final-file_administrativeactfile_modification', FinalForm)], url_name='file_administrativeactfile_modification',) file_administrativeactfile_deletion_wizard = AdministrativeActDeletionWizard([ ('selec-file_administrativeactfile_deletion', AdministrativeActFileFormSelection), ('final-file_administrativeactfile_deletion', FinalAdministrativeActDeleteForm)], url_name='file_administrativeactfile_deletion',) class OperationWizard(FileWizard): model = models.Operation object_parcel_type = 'operation' def process_post_request(self, request, storage, *args, **kwargs): """ Change the town (and parcel) form to a free selection town's (parcel's) form if no archaelogical file is provided """ #!TODO manage with condition list file_key = 'general-' + self.url_name + '-associated_file' if file_key in request.POST.keys(): town_form_key = 'towns-' + self.url_name parcel_form_key = 'parcels-' + self.url_name if request.POST[file_key]: self.form_list[unicode(town_form_key)] = SelectedTownFormSet self.form_list[unicode(parcel_form_key)] = SelectedParcelFormSet else: self.form_list[unicode(town_form_key)] = \ SelectedTownGeneralFormSet self.form_list[unicode(parcel_form_key)] = \ SelectedParcelGeneralFormSet return super(OperationWizard, self).process_post_request(request, storage, *args, **kwargs) def get_form(self, request, storage, step=None, data=None, files=None): """ Manage specifics fields """ if data: data = data.copy() else: data = {} if not step: step = self.determine_step(request, storage) form = self.get_form_list(request, storage)[step] general_form_key = 'general-' + self.url_name # put hidden year field for refs if data and step.startswith('refs-') \ and self.session_has_key(request, storage, general_form_key): prefix = 'refs-' + self.url_name year = int(request.session[storage.prefix]['step_data']\ [general_form_key][general_form_key+"-year"]) data[prefix+'-hidden_year'] = year data[prefix+'-hidden_ope'] = True # manage the dynamic choice of towns if step.startswith('towns-') and hasattr(form, 'management_form') \ and self.session_has_key(request, storage, general_form_key): towns = [] file_id = self.session_get_value(request, storage, general_form_key, "associated_file") if file_id: try: for town in models.File.objects.get(pk=int(file_id) ).towns.all(): towns.append((town.pk, unicode(town))) except (ValueError, ObjectDoesNotExist): pass data['TOWNS'] = sorted(towns, key=lambda x:x[1]) elif step.startswith('parcels-') and hasattr(form, 'management_form'): file_id = self.session_get_value(request, storage, general_form_key, "associated_file") if file_id: parcels = [] try: for parcel in models.File.objects.get(pk=int(file_id) ).parcels.all(): parcels.append((parcel.pk, unicode(parcel))) except (ValueError, ObjectDoesNotExist): pass data['PARCELS'] = sorted(parcels, key=lambda x:x[1]) else: towns = [] town_form_key = 'towns-' + self.url_name town_ids = self.session_get_value(request, storage, town_form_key, 'town', multi=True) for town_id in town_ids: try: town = models.Town.objects.get(pk=int(town_id)) towns.append((town.pk, unicode(town))) except (ValueError, ObjectDoesNotExist): pass data['TOWNS'] = sorted(towns, key=lambda x:x[1]) data = data or None form = super(OperationWizard, self).get_form(request, storage, step, data, files) return form def get_extra_model(self, dct, request, storage, form_list): dct['history_modifier'] = request.user return dct def get_form_initial(self, request, storage, step): initial = super(OperationWizard, self).get_form_initial(request, storage, step) if initial: return initial # put hidden year and default operation_code field for refs general_form_key = 'general-' + self.url_name initial = {} if step.startswith('refs-') \ and self.session_has_key(request, storage, general_form_key): year = int(request.session[storage.prefix]['step_data']\ [general_form_key][general_form_key+"-year"]) initial['hidden_year'] = year max_val = models.Operation.objects.filter(year=year).aggregate( Max('operation_code'))["operation_code__max"] initial['operation_code'] = max_val and (max_val + 1) or 1 return initial def get_formated_datas(self, forms): """ Show a specific warning if no archaelogical file is provided """ datas = super(OperationWizard, self).get_formated_datas(forms) # simple selection town is used if no Archaelogical File is provided has_af = [form for form in forms if isinstance(form, SelectedTownFormSet)] if not has_af: datas = [[_(u"Warning: No Archaelogical File is provided. " u"If you have forget it return to the first step."), []]]\ + datas return datas class OperationSelect(forms.Form): towns = forms.IntegerField(label=_(u"Town"), widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ 'autocomplete-town', associated_model=models.Town), validators=[models.valid_id(models.Town)]) operation_type = forms.ChoiceField(label=_("Operation type"), choices=models.OperationType.get_types()) remains = forms.ChoiceField(label=_("Remains"), choices=models.RemainType.get_types()) year = forms.IntegerField(label=_("Year")) class OperationFormSelection(forms.Form): form_label = _("Operation") 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), validators=[models.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 OperationFormGeneral(forms.Form): form_label = _("General") associated_models = {'in_charge':models.Person, 'associated_file':models.File, 'operation_type':models.OperationType} currents = {'associated_file':models.File} in_charge = forms.IntegerField(label=_("Person in charge of the operation"), widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-person', args=["_".join( [unicode(models.PersonType.objects.get(txt_idx='head_scientist').pk), unicode(models.PersonType.objects.get(txt_idx='sra_agent').pk)])]), associated_model=models.Person), validators=[models.valid_id(models.Person)], required=False) associated_file = forms.IntegerField(label=_("Archaelogical file"), widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-file'), associated_model=models.File), validators=[models.valid_id(models.File)], required=False) operation_type = forms.ChoiceField(label=_("Operation type"), choices=models.OperationType.get_types()) start_date = forms.DateField(label=_(u"Start date"), required=False, widget=widgets.JQueryDate) surface = forms.IntegerField(required=False, widget=widgets.AreaWidget, label=_(u"Total surface"), validators=[validators.MinValueValidator(0), validators.MaxValueValidator(999999999)]) year = forms.IntegerField(label=_("Year"), initial=lambda:datetime.datetime.now().year, validators=[validators.MinValueValidator(1900), validators.MaxValueValidator(2100)]) comment = forms.CharField(label=_(u"Comment"), widget=forms.Textarea, required=False) class OperationFormReference(forms.Form): form_label = _("References") associated_models = {'in_charge':models.Person, 'associated_file':models.File, 'operation_type':models.OperationType} currents = {'associated_file':models.File} hidden_year = forms.IntegerField(widget=forms.HiddenInput) hidden_ope = forms.BooleanField(widget=forms.HiddenInput, required=False) operation_code = forms.IntegerField(label=_(u"Operation code")) 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)]) def clean(self): cleaned_data = self.cleaned_data if 'hidden_ope' in cleaned_data and cleaned_data['hidden_ope']: return cleaned_data year = cleaned_data.get("hidden_year") operation_code = cleaned_data.get("operation_code") ops = models.Operation.objects.filter(year=year, operation_code=operation_code).count() if ops: max_val = models.Operation.objects.filter(year=year).aggregate( Max('operation_code'))["operation_code__max"] raise forms.ValidationError(_(u"Operation code already exist for " "year: %(year)d - use a value bigger than %(last_val)d") % {'year':year, 'last_val':max_val}) return cleaned_data class OperationFormPreventive(forms.Form): form_label = _("Preventive informations - excavation") cost = forms.IntegerField(label=_(u"Cost"), 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':models.Town} town = forms.ChoiceField(label=_("Town"), choices=(), validators=[models.valid_id(models.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: self.fields['town'].choices = [('', '--')] + towns SelectedTownFormSet = formset_factory(SelectedTownForm, can_delete=True, formset=TownFormSet) SelectedTownFormSet.form_label = _("Towns") SelectedTownGeneralFormSet = formset_factory(TownForm, can_delete=True, formset=TownFormSet) SelectedTownGeneralFormSet.form_label = _("Towns") class SelectedParcelForm(forms.Form): form_label = _("Parcels") associated_models = {'parcel':models.Parcel} parcel = forms.ChoiceField(label=_("Parcel"), choices=(), validators=[models.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") 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 clean_duplicated(self, ['remain_type']) RemainFormset = formset_factory(RemainForm, can_delete=True, formset=RemainFormSet) RemainFormset.form_label = _("Remain types") class PeriodForm(forms.Form): form_label = _("Periods") 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 clean_duplicated(self, ['period']) PeriodFormset = formset_factory(PeriodForm, can_delete=True, formset=PeriodFormSet) PeriodFormset.form_label = _("Periods") operation_search_wizard = SearchWizard([ ('general-operation_search', OperationFormSelection)], url_name='operation_search',) operation_creation_wizard = OperationWizard([ ('general-operation_creation', OperationFormGeneral), ('refs-operation_creation', OperationFormReference), ('preventive-operation_creation', OperationFormPreventive), ('preventivediag-operation_creation', OperationFormPreventiveDiag), ('towns-operation_creation', SelectedTownFormSet), ('parcels-operation_creation', SelectedParcelFormSet), ('remains-operation_creation', RemainFormset), ('periods-operation_creation', PeriodFormset), ('final-operation_creation', FinalForm)], condition_list={ 'preventive-operation_creation':is_preventive('general-operation_creation', models.OperationType, 'operation_type', 'prev_excavation'), 'preventivediag-operation_creation':is_preventive('general-operation_creation', models.OperationType, 'operation_type', 'arch_diagnostic') }, url_name='operation_creation',) operation_modification_wizard = OperationWizard([ ('selec-operation_modification', OperationFormSelection), ('general-operation_modification', OperationFormGeneral), ('refs-operation_modification', OperationFormReference), ('preventive-operation_modification', OperationFormPreventive), ('preventivediag-operation_modification', OperationFormPreventiveDiag), ('towns-operation_modification', SelectedTownFormSet), ('parcels-operation_modification', SelectedParcelFormSet), ('remains-operation_modification', RemainFormset), ('periods-operation_modification', PeriodFormset), ('final-operation_modification', FinalForm)], condition_list={ 'preventive-operation_modification':is_preventive( 'general-operation_modification', models.OperationType, 'operation_type', 'prev_excavation'), 'preventivediag-operation_modification':is_preventive( 'general-operation_modification', models.OperationType, 'operation_type', 'arch_diagnostic') }, url_name='operation_modification',) class OperationDateFormSelection(forms.Form): form_label = _("Closing date") end_date = forms.DateField(label=_(u"Closing date"), widget=widgets.JQueryDate) class OperationClosingWizard(DeletionWizard): model = models.Operation fields = ['year', 'operation_code', 'operation_type', 'associated_file', 'in_charge', 'start_date', 'end_date', 'comment', 'towns', 'remains'] class FinalOperationClosingForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to close this operation?") operation_closing_wizard = OperationClosingWizard([ ('selec-operation_closing', OperationFormSelection), ('date-operation_closing', OperationDateFormSelection), ('final-operation_closing', FinalOperationClosingForm)], url_name='operation_closing',) class OperationDeletionForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to delete this operation?") operation_deletion_wizard = DeletionWizard([ ('selec-operation_deletion', OperationFormSelection), ('final-operation_deletion', OperationDeletionForm)], url_name='operation_deletion',) class OperationAdministrativeActWizard(FileAdministrativeActWizard): model = models.Operation class OperationEditAdministrativeActWizard(FileAdministrativeActWizard): model = models.AdministrativeAct edit = True def get_associated_item(self, request, storage, dct): return self.get_current_object(request, storage).operation class AdministrativeActOpeSelect(forms.Form): operation__towns = forms.IntegerField(label=_(u"Town"), widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ 'autocomplete-town', associated_model=models.Town), validators=[models.valid_id(models.Town)]) act_type = forms.ChoiceField(label=_("Act type"), choices=models.ActType.get_types(dct={'intented_to':'O'})) class AdministrativeActOpeFormSelection(AdministrativeActFileFormSelection): pk = forms.IntegerField(label="", required=False, widget=widgets.JQueryJqGrid(reverse_lazy('get-administrativeactop'), AdministrativeActOpeSelect(), models.AdministrativeAct, table_cols='TABLE_COLS_OPE'), validators=[models.valid_id(models.AdministrativeAct)]) class AdministrativeActOpeForm(AdministrativeActForm): form_label = _("General") act_type = forms.ChoiceField(label=_("Act type"), choices=models.ActType.get_types(dct={'intented_to':'O'})) operation_administrativeactop_wizard = OperationAdministrativeActWizard([ ('selec-operation_administrativeactop', OperationFormSelection), ('administrativeact-operation_administrativeactop', AdministrativeActOpeForm), ('final-operation_administrativeactop', FinalForm)], url_name='operation_administrativeactop',) operation_administrativeactop_modification_wizard = \ OperationEditAdministrativeActWizard([ ('selec-operation_administrativeactop_modification', AdministrativeActOpeFormSelection), ('administrativeact-operation_administrativeactop_modification', AdministrativeActOpeForm), ('final-operation_administrativeactop_modification', FinalForm)], url_name='operation_administrativeactop_modification',) operation_administrativeactop_deletion_wizard = AdministrativeActDeletionWizard([ ('selec-operation_administrativeactop_deletion', AdministrativeActOpeFormSelection), ('final-operation_administrativeactop_deletion', FinalAdministrativeActDeleteForm)], url_name='operation_administrativeactop_deletion',) class RecordWizard(Wizard): model = models.Operation edit = False def get_form(self, request, storage, step=None, data=None, files=None): """ Get associated operation """ if data: data = data.copy() else: data = {} if not step: step = self.determine_step(request, storage) form = self.get_form_list(request, storage)[step] current_object = self.get_current_object(request, storage) if step.startswith('general-') and current_object: data[self.model._meta.object_name] = current_object form = super(RecordWizard, self).get_form(request, storage, step, data, files) return form def get_saved_model(self): return models.ContextRecord def get_current_saved_object(self, request, storage): """ Permit a distinguo when saved model is not the base selected model """ if self.model == models.Operation: return None return self.get_current_object(request, storage) def save_model(self, dct, m2m, request, storage, form_list, return_object): if 'pk' in dct: dct.pop('pk') return super(RecordWizard, self).save_model(dct, m2m, request, storage, form_list, return_object) class RecordModifWizard(RecordWizard): model = models.ContextRecord class RecordSelect(forms.Form): parcel__town = forms.IntegerField(label=_(u"Town"), widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ 'autocomplete-town', associated_model=models.Town), validators=[models.valid_id(models.Town)]) parcel__operation__year = forms.IntegerField(label=_("Year")) datings__period = forms.ChoiceField(label=_("Period"), choices=models.Period.get_types()) unit = forms.ChoiceField(label=_("Unit type"), choices=models.Unit.get_types()) class RecordFormSelection(forms.Form): form_label = _("Context record") associated_models = {'pk':models.ContextRecord} currents = {'pk':models.ContextRecord} pk = forms.IntegerField(label="", required=False, widget=widgets.JQueryJqGrid(reverse_lazy('get-contextrecord'), RecordSelect(), models.ContextRecord), validators=[models.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 select a context " u"record.")) return cleaned_data class RecordFormGeneral(forms.Form): form_label = _("General") associated_models = {'parcel':models.Parcel, 'unit':models.Unit} parcel = forms.ChoiceField(label=_("Parcel"), choices=[]) label = forms.CharField(label=_(u"Label"), validators=[validators.MaxLengthValidator(200)]) description = forms.CharField(label=_(u"Description"), widget=forms.Textarea, required=False) length = forms.IntegerField(label=_(u"Length"), required=False) width = forms.IntegerField(label=_(u"Width"), required=False) thickness = forms.IntegerField(label=_(u"Thickness"), required=False) depth = forms.IntegerField(label=_(u"Depth"), required=False) unit = forms.ChoiceField(label=_("Unit"), required=False, choices=models.Unit.get_types()) location = forms.CharField(label=_(u"Location"), widget=forms.Textarea, required=False, validators=[validators.MaxLengthValidator(200)]) def __init__(self, *args, **kwargs): operation = None if 'data' in kwargs and kwargs['data'] and \ ('Operation' in kwargs['data'] or 'ContextRecord' in kwargs['data']): if 'Operation' in kwargs['data']: operation = kwargs['data']['Operation'] if 'ContextRecord' in kwargs['data']: operation = kwargs['data']['ContextRecord'].parcel.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) self.fields['parcel'].choices = [('', '--')] if operation: 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(key), [(parcel.pk, parcel.short_label()) for parcel in gparcels]) ) class DatingForm(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=models.Period.get_types()) 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=models.DatingQuality.get_types()) dating_type = forms.ChoiceField(label=_("Dating type"), required=False, choices=models.DatingType.get_types()) DatingFormSet = formset_factory(DatingForm, can_delete=True, formset=FormSet) DatingFormSet.form_label = _("Dating") class RecordFormInterpretation(forms.Form): form_label = _("Interpretation") has_furniture = forms.NullBooleanField(label=_(u"Has furniture?"), required=False) filling = forms.CharField(label=_(u"Filling"), widget=forms.Textarea, required=False) interpretation = forms.CharField(label=_(u"Interpretation"), widget=forms.Textarea, required=False) 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) record_search_wizard = SearchWizard([ ('general-record_search', RecordFormSelection)], url_name='record_search',) record_creation_wizard = RecordWizard([ ('selec-record_creation', OperationFormSelection), ('general-record_creation', RecordFormGeneral), ('datings-record_creation', DatingFormSet), ('interpretation-record_creation', RecordFormInterpretation), ('final-record_creation', FinalForm)], url_name='record_creation',) record_modification_wizard = RecordModifWizard([ ('selec-record_modification', RecordFormSelection), ('general-record_modification', RecordFormGeneral), ('datings-record_modification', DatingFormSet), ('interpretation-record_modification', RecordFormInterpretation), ('final-record_modification', FinalForm)], url_name='record_modification',) class RecordDeletionWizard(DeletionWizard): model = models.ContextRecord fields = ['label', 'parcel', 'description', 'length', 'width', 'thickness', 'depth', 'location', 'datings', 'units', 'has_furniture', 'filling', 'interpretation', 'taq', 'taq_estimated', 'tpq', 'tpq_estimated'] class RecordDeletionForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to delete this context record?") record_deletion_wizard = RecordDeletionWizard([ ('selec-record_deletion', RecordFormSelection), ('final-record_deletion', RecordDeletionForm)], url_name='record_deletion',) class ItemWizard(Wizard): model = models.Item def get_extra_model(self, dct, request, storage, form_list): dct = super(ItemWizard, self).get_extra_model(dct, request, storage, form_list) dct['order'] = 1 if 'pk' in dct and type(dct['pk']) == models.ContextRecord: dct['base_items__context_record'] = dct.pop('pk') return dct class ItemFormBase(forms.Form): form_label = _("Base item") base_model = 'base_items' base_items__label = forms.CharField(label=_(u"General label"), validators=[validators.MaxLengthValidator(60)]) base_items__description = forms.CharField(label=_("General description"), widget=forms.Textarea) base_items__is_isolated = forms.NullBooleanField(label=_(u"Is isolated?"), required=False) class ItemForm(forms.Form): form_label = _("Item") associated_models = {'material_type':models.MaterialType,} label = forms.CharField(label=_(u"Label"), validators=[validators.MaxLengthValidator(60)]) description = forms.CharField(label=_("Precise description"), widget=forms.Textarea, required=False) material_type = forms.ChoiceField(label=_("Material type"), choices=models.MaterialType.get_types()) volume = forms.IntegerField(label=_(u"Volume"), required=False) weight = forms.IntegerField(label=_(u"Weight"), required=False) item_number = forms.IntegerField(label=_(u"Item number"), required=False) class DateForm(forms.Form): form_label = _("Dating") base_model = 'dating' associated_models = {'dating__dating_type':models.DatingType, 'dating__quality':models.DatingQuality, 'dating__period':models.Period} dating__period = forms.ChoiceField(label=_("Period"), choices=models.Period.get_types()) dating__start_date = forms.IntegerField(label=_(u"Start date"), required=False) dating__end_date = forms.IntegerField(label=_(u"End date"), required=False) dating__quality = forms.ChoiceField(label=_("Quality"), required=False, choices=models.DatingQuality.get_types()) dating__dating_type = forms.ChoiceField(label=_("Dating type"), required=False, choices=models.DatingType.get_types()) item_creation_wizard = ItemWizard([ ('selecrecord-item_creation', RecordFormSelection), ('base_item-record_creation', ItemFormBase), ('item-item_creation', ItemForm), ('dating-item_creation', DateForm), ('final-item_creation', FinalForm)], url_name='item_creation',) class ItemSelect(forms.Form): base_items__context_record__parcel__town = forms.IntegerField( widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ 'autocomplete-town', associated_model=models.Town), validators=[models.valid_id(models.Town)], label=_(u"Town")) base_items__context_record__parcel__operation__year = forms.IntegerField( label=_("Year")) dating__period = forms.ChoiceField( label=_("Period"), choices=models.Period.get_types()) material_type = forms.ChoiceField( label=_("Material type"), choices=models.MaterialType.get_types()) base_items__is_isolated = forms.NullBooleanField(label=_("Is isolated?")) class ItemFormSelection(forms.Form): form_label = _("Item") associated_models = {'pk':models.Item} currents = {'pk':models.Item} pk = forms.IntegerField(label="", required=False, widget=widgets.JQueryJqGrid(reverse_lazy('get-item'), ItemSelect(), models.Item), validators=[models.valid_id(models.Item)]) item_search_wizard = SearchWizard([ ('general-item_search', ItemFormSelection)], url_name='item_search',) item_modification_wizard = ItemWizard([ ('selec-item_modification', ItemFormSelection), ('item-item_modification', ItemForm), ('dating-item_modification', DateForm), ('final-item_modification', FinalForm)], url_name='item_modification',) class TreatmentWizard(Wizard): model = models.Treatment class BaseTreatmentForm(forms.Form): form_label = _(u"Base treatment") associated_models = {'treatment_type':models.TreatmentType, 'person':models.Person, 'location':models.Warehouse} treatment_type = forms.ChoiceField(label=_(u"Treatment type"), choices=models.TreatmentType.get_types()) description = forms.CharField(label=_(u"Description"), widget=forms.Textarea, required=False) person = forms.IntegerField(label=_(u"Person"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-person'), associated_model=models.Person), validators=[models.valid_id(models.Person)]) location = forms.IntegerField(label=_(u"Location"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-warehouse'), associated_model=models.Warehouse, new=True), validators=[models.valid_id(models.Warehouse)]) start_date = forms.DateField(label=_(u"Start date"), required=False, widget=widgets.JQueryDate) end_date = forms.DateField(label=_(u"End date"), required=False, widget=widgets.JQueryDate) class ItemMultipleFormSelection(forms.Form): form_label = _(u"Upstream items") associated_models = {'items':models.Item} items = forms.IntegerField(label="", required=False, widget=widgets.JQueryJqGrid(reverse_lazy('get-item'), ItemSelect(), models.Item, multiple=True), validators=[models.valid_id(models.Item)]) class ContainerForm(forms.Form): form_label = _(u"Container") associated_models = {'container_type':models.ContainerType,} reference = forms.CharField(label=_(u"Reference")) container_type = forms.ChoiceField(label=_(u"Container type"), choices=models.ContainerType.get_types()) comment = forms.CharField(label=_(u"Comment"), widget=forms.Textarea, required=False) def check_treatment(form_name, type_key, type_list=[], not_type_list=[]): type_list = [models.TreatmentType.objects.get(txt_idx=tpe).pk for tpe in type_list] not_type_list = [models.TreatmentType.objects.get(txt_idx=tpe).pk for tpe in not_type_list] def func(self, request, storage): if storage.prefix not in request.session or \ 'step_data' not in request.session[storage.prefix] or \ form_name not in request.session[storage.prefix]['step_data'] or\ form_name + '-' + type_key not in \ request.session[storage.prefix]['step_data'][form_name]: return False try: type = int(request.session[storage.prefix]['step_data']\ [form_name][form_name+'-'+type_key]) return (not type_list or type in type_list) \ and type not in not_type_list except ValueError: return False return func class ResultItemForm(forms.Form): form_label = _("Resulting item") associated_models = {'material_type':models.MaterialType} label = forms.CharField(label=_(u"Label"), validators=[validators.MaxLengthValidator(60)]) description = forms.CharField(label=_("Precise description"), widget=forms.Textarea) material_type = forms.ChoiceField(label=_("Material type"), choices=models.MaterialType.get_types()) volume = forms.IntegerField(label=_(u"Volume")) weight = forms.IntegerField(label=_(u"Weight")) item_number = forms.IntegerField(label=_(u"Item number")) ResultItemFormSet = formset_factory(ResultItemForm, can_delete=True, formset=FormSet) ResultItemFormSet.form_label = _(u"Resulting items") class UpstreamItemFormSelection(ItemFormSelection): form_label = _(u"Upstream item") treatment_creation_wizard = TreatmentWizard([ ('basetreatment-treatment_creation', BaseTreatmentForm), ('selecitem-treatment_creation', UpstreamItemFormSelection), ('multiselecitems-treatment_creation', ItemMultipleFormSelection), ('container-treatment_creation', ContainerForm), ('resultitem-treatment_creation', ResultItemForm), ('resultitems-treatment_creation', ResultItemFormSet), ('final-treatment_creation', FinalForm)], condition_list={ 'selecitem-treatment_creation': check_treatment('basetreatment-treatment_creation', 'treatment_type', not_type_list=['physical_grouping']), 'multiselecitems-treatment_creation': check_treatment('basetreatment-treatment_creation', 'treatment_type', ['physical_grouping']), 'resultitems-treatment_creation': check_treatment('basetreatment-treatment_creation', 'treatment_type', ['split']), 'resultitem-treatment_creation': check_treatment('basetreatment-treatment_creation', 'treatment_type', not_type_list=['split']), 'container-treatment_creation': check_treatment('basetreatment-treatment_creation', 'treatment_type', ['packaging']), }, url_name='treatment_creation',)