diff options
-rw-r--r-- | archaeological_context_records/models.py | 7 | ||||
-rw-r--r-- | archaeological_context_records/wizards.py | 3 | ||||
-rw-r--r-- | archaeological_files/models.py | 2 | ||||
-rw-r--r-- | archaeological_files/views.py | 4 | ||||
-rw-r--r-- | archaeological_files/wizards.py | 51 | ||||
-rw-r--r-- | archaeological_finds/models.py | 8 | ||||
-rw-r--r-- | archaeological_operations/forms.py | 1 | ||||
-rw-r--r-- | archaeological_operations/models.py | 10 | ||||
-rw-r--r-- | archaeological_operations/wizards.py | 12 | ||||
-rw-r--r-- | ishtar_common/models.py | 15 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/wizard/search.html (renamed from ishtar_common/templates/search.html) | 13 | ||||
-rw-r--r-- | ishtar_common/urls.py | 2 | ||||
-rw-r--r-- | ishtar_common/wizards.py | 878 |
13 files changed, 946 insertions, 60 deletions
diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py index 75653e78e..64772627b 100644 --- a/archaeological_context_records/models.py +++ b/archaeological_context_records/models.py @@ -143,7 +143,7 @@ class ContextRecord(BaseHistorizedItem, OwnPerms): return self.short_label() def short_label(self): - return JOINT.join([unicode(item) for item in [self.parcel, + return settings.JOINT.join([unicode(item) for item in [self.parcel, self.label] if item]) def full_label(self): @@ -154,13 +154,14 @@ class ContextRecord(BaseHistorizedItem, OwnPerms): def _real_label(self): if not self.parcel.operation.code_patriarche: return - return JOINT.join((self.parcel.operation.code_patriarche, + return settings.JOINT.join((self.parcel.operation.code_patriarche, self.label)) def _temp_label(self): if self.parcel.operation.code_patriarche: return - return JOINT.join([unicode(lbl) for lbl in [self.parcel.operation.year, + return settings.JOINT.join([unicode(lbl) for lbl in [ + self.parcel.operation.year, self.parcel.operation.operation_code, self.label] if lbl]) diff --git a/archaeological_context_records/wizards.py b/archaeological_context_records/wizards.py index b14272e16..1fd657bcc 100644 --- a/archaeological_context_records/wizards.py +++ b/archaeological_context_records/wizards.py @@ -73,7 +73,8 @@ class RecordWizard(Wizard): else: data = {} if not step: - step = self.determine_step(request, storage) + step = self.steps.current + #step = self.determine_step(request, storage) form = self.get_form_list(request, storage)[step] general_form_key = 'general-' + self.url_name diff --git a/archaeological_files/models.py b/archaeological_files/models.py index c3d950d3e..0bc9a9566 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -131,7 +131,7 @@ class File(BaseHistorizedItem, OwnPerms): unicode(self.numeric_reference)))) items += [unicode(getattr(self, k))[:36] for k in ['internal_reference',] if getattr(self, k)] - return JOINT.join(items) + return settings.JOINT.join(items) @classmethod def get_query_owns(cls, user): diff --git a/archaeological_files/views.py b/archaeological_files/views.py index 0f71b24d8..84a42b95a 100644 --- a/archaeological_files/views.py +++ b/archaeological_files/views.py @@ -78,8 +78,8 @@ def dashboard_file(request, dct, obj_id=None, *args, **kwargs): context_instance=RequestContext(request)) file_search_wizard = SearchWizard.as_view( - [('general-file_search', FileFormSelection)], - url_name='file_search',) + [('general-file_search', FileFormSelection)], + url_name='file_search',) file_creation_wizard = FileWizard.as_view([ ('general-file_creation', FileFormGeneral), ('address-file_creation', FileFormAddress), diff --git a/archaeological_files/wizards.py b/archaeological_files/wizards.py index b161deae0..e98248965 100644 --- a/archaeological_files/wizards.py +++ b/archaeological_files/wizards.py @@ -19,6 +19,7 @@ from django.conf import settings from django.core.exceptions import ObjectDoesNotExist +from django.db.models import Max from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.translation import ugettext_lazy as _ @@ -26,14 +27,15 @@ from django.utils.translation import ugettext_lazy as _ from ishtar_common.wizards import Wizard, ClosingWizard from archaeological_operations.wizards import OperationAdministrativeActWizard,\ AdministrativeActDeletionWizard -from archaeological_operations.models import AdministrativeAct +from ishtar_common.models import Town +from archaeological_operations.models import AdministrativeAct, Parcel import models class FileWizard(Wizard): model = models.File object_parcel_type = 'associated_file' - def get_form(self, request, storage, step=None, data=None, files=None): + def get_form(self, step=None, data=None, files=None): """ Manage towns """ @@ -43,40 +45,40 @@ class FileWizard(Wizard): data = {} # manage the dynamic choice of towns if not step: - step = self.determine_step(request, storage) - form = self.get_form_list(request, storage)[step] + # step = self.determine_step(request, storage) + step = self.steps.current + form = self.get_form_list()[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): + and self.session_has_key(self.request, self.storage, town_form_key): towns = [] - qdict = request.session[storage.prefix]['step_data'][town_form_key] + qdict = self.request.session[self.storage.prefix]['step_data']\ + [town_form_key] for k in qdict.keys(): if k.endswith("town") and qdict[k]: try: - town = Town.objects.get(pk=int(qdict[k])) + town = Town.objects.get(pk=int(qdict[k][0])) 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) + form = super(FileWizard, self).get_form(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) + def get_extra_model(self, dct, form_list): + dct = super(FileWizard, self).get_extra_model(dct, form_list) if not dct['numeric_reference']: 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): + def done(self, form_list, **kwargs): ''' Save parcels ''' - r = super(FileWizard, self).done(request, storage, form_list, - return_object=True, **kwargs) + r = super(FileWizard, self).done(form_list, return_object=True, + **kwargs) if type(r) not in (list, tuple) or len(r) != 2: return r obj, res = r @@ -92,7 +94,7 @@ class FileWizard(Wizard): dct = frm.cleaned_data.copy() if 'parcel' in dct: try: - parcel = models.Parcel.objects.get(pk=dct['parcel']) + parcel = Parcel.objects.get(pk=dct['parcel']) setattr(parcel, self.object_parcel_type, obj) parcel.save() except (ValueError, ObjectDoesNotExist): @@ -106,10 +108,10 @@ class FileWizard(Wizard): dct[self.object_parcel_type] = obj if 'DELETE' in dct: dct.pop('DELETE') - parcel = models.Parcel.objects.filter(**dct).count() + parcel = Parcel.objects.filter(**dct).count() if not parcel: - dct['history_modifier'] = request.user - parcel = models.Parcel(**dct) + dct['history_modifier'] = self.request.user + parcel = Parcel(**dct) parcel.save() return res @@ -125,6 +127,7 @@ class FileClosingWizard(ClosingWizard): if settings.COUNTRY == 'fr': fields += ['saisine_type', 'reference_number'] fields += ['towns'] + class FileDeletionWizard(FileClosingWizard): def get_formated_datas(self, forms): datas = super(FileDeletionWizard, self).get_formated_datas(forms) @@ -135,14 +138,14 @@ class FileDeletionWizard(FileClosingWizard): datas[-1][1].append(('', unicode(operation))) return datas - def done(self, request, storage, form_list, **kwargs): - obj = self.get_current_object(request, storage) + def done(self, form_list, **kwargs): + obj = self.get_current_object() 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)) + context_instance=RequestContext(self.request)) class FileAdministrativeActWizard(OperationAdministrativeActWizard): @@ -151,5 +154,5 @@ class FileAdministrativeActWizard(OperationAdministrativeActWizard): class FileEditAdministrativeActWizard(FileAdministrativeActWizard): model = AdministrativeAct edit = True - def get_associated_item(self, request, storage, dct): - return self.get_current_object(request, storage).associated_file + def get_associated_item(self, dct): + return self.get_current_object().associated_file diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py index eeb293934..cb305a93a 100644 --- a/archaeological_finds/models.py +++ b/archaeological_finds/models.py @@ -79,14 +79,14 @@ class BaseFind(BaseHistorizedItem, OwnPerms): finds += [ope.code_patriarche or \ (unicode(ope.year) + "-" + unicode(ope.operation_code))] finds += [self.context_record.label, unicode(self.material_index)] - return JOINT.join(finds) + return settings.JOINT.join(finds) def _real_label(self): if not self.context_record.parcel.operation.code_patriarche: return find = self.get_last_find() lbl = find.label or self.label - return JOINT.join([unicode(it) for it in ( + return settings.JOINT.join([unicode(it) for it in ( self.context_record.parcel.operation.code_patriarche, self.context_record.label, lbl) if it]) @@ -96,7 +96,7 @@ class BaseFind(BaseHistorizedItem, OwnPerms): return find = self.get_last_find() lbl = find.label or self.label - return JOINT.join([unicode(it) for it in ( + return settings.JOINT.join([unicode(it) for it in ( self.context_record.parcel.year, self.index, self.context_record.label, @@ -278,5 +278,5 @@ class Property(LightHistorizedItem): verbose_name_plural = _(u"Properties") def __unicode__(self): - return self.person + JOINT + self.find + return self.person + settings.JOINT + self.find diff --git a/archaeological_operations/forms.py b/archaeological_operations/forms.py index d87e72423..dc342f477 100644 --- a/archaeological_operations/forms.py +++ b/archaeological_operations/forms.py @@ -29,6 +29,7 @@ from django.template import RequestContext 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.utils.translation import ugettext_lazy as _ from ishtar_common.models import valid_id, PersonType, Person, Town diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index 2c008ef9b..60de936d2 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -129,7 +129,7 @@ class Operation(BaseHistorizedItem, OwnPerms): items[0] = unicode(self.towns.all()[0]) items.append("-".join((unicode(self.year), unicode(self.operation_code)))) - return JOINT.join(items) + return settings.JOINT.join(items) @classmethod def get_available_operation_code(cls, year=None): @@ -301,7 +301,7 @@ related_name='+', verbose_name=_(u"Person in charge of the scientific part")) ) def __unicode__(self): - return JOINT.join([unicode(item) + return settings.JOINT.join([unicode(item) for item in [self.operation, self.associated_file, self.act_object] if item]) @@ -322,7 +322,7 @@ class Parcel(LightHistorizedItem): verbose_name_plural = _(u"Parcels") def short_label(self): - return JOINT.join([unicode(item) for item in [self.section, + return settings.JOINT.join([unicode(item) for item in [self.section, self.parcel_number] if item]) def __unicode__(self): @@ -332,7 +332,7 @@ class Parcel(LightHistorizedItem): items = [unicode(self.operation or self.associated_file)] items += [unicode(item) for item in [self.section, self.parcel_number] if item] - return JOINT.join(items) + return settings.JOINT.join(items) class ParcelOwner(LightHistorizedItem): owner = models.ForeignKey(Person, verbose_name=_(u"Owner")) @@ -345,7 +345,7 @@ class ParcelOwner(LightHistorizedItem): verbose_name_plural = _(u"Parcel owners") def __unicode__(self): - return self.owner + JOINT + self.parcel + return self.owner + settings.JOINT + self.parcel class OperationDashboard: def __init__(self): diff --git a/archaeological_operations/wizards.py b/archaeological_operations/wizards.py index df785fe6e..542d0118b 100644 --- a/archaeological_operations/wizards.py +++ b/archaeological_operations/wizards.py @@ -45,7 +45,8 @@ class OperationWizard(Wizard): """ context = super(OperationWizard, self).get_extra_context(request, storage) - step = self.determine_step(request, storage) + #step = self.determine_step(request, storage) + step = self.steps.current if not step.startswith('towns-'): return context context['TOWNS'] = self.get_towns(request, storage) @@ -79,7 +80,8 @@ class OperationWizard(Wizard): else: data = {} if not step: - step = self.determine_step(request, storage) + #step = self.determine_step(request, storage) + step = self.steps.current form = self.get_form_list(request, storage)[step] general_form_key = 'general-' + self.url_name # manage the dynamic choice of towns @@ -229,7 +231,9 @@ class AdministrativeActDeletionWizard(ClosingWizard): context_instance=RequestContext(request)) def is_preventive(form_name, model, type_key='operation_type', key=''): - def func(self, request, storage): + def func(self): + request = self.request + storage = self.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\ @@ -238,7 +242,7 @@ def is_preventive(form_name, model, type_key='operation_type', key=''): return False try: typ = int(request.session[storage.prefix]['step_data']\ - [form_name][form_name+'-'+type_key]) + [form_name][form_name+'-'+type_key][0]) return model.is_preventive(typ, key) except ValueError: return False diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 9b6f94907..3ca830aa3 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -38,8 +38,6 @@ from django.contrib import admin from simple_history.models import HistoricalRecords as BaseHistoricalRecords -JOINT = u" - " - def post_save_user(sender, **kwargs): user = kwargs['instance'] ishtaruser = None @@ -499,7 +497,7 @@ class Department(models.Model): ordering = ['number'] def __unicode__(self): - return unicode(self.number) + JOINT + self.label + return unicode(self.number) + settings.JOINT + self.label class Address(BaseHistorizedItem): address = models.TextField(_(u"Address"), null=True, blank=True) @@ -574,11 +572,10 @@ class Person(Address, OwnPerms) : def __unicode__(self): lbl = u"%s %s" % (self.name, self.surname) - lbl += JOINT if self.attached_to: - lbl += unicode(self.attached_to) + lbl += settings.JOINT + unicode(self.attached_to) elif self.email: - lbl += self.email + lbl += settings.JOINT + self.email return lbl def full_label(self): @@ -624,7 +621,7 @@ class Author(models.Model): verbose_name_plural = _(u"Authors") def __unicode__(self): - return unicode(self.person) + JOINT + unicode(self.author_type) + return unicode(self.person) + settings.JOINT + unicode(self.author_type) class SourceType(GeneralType): class Meta: @@ -655,14 +652,14 @@ if settings.COUNTRY == 'fr': department = models.ForeignKey(Department, verbose_name=u"Département") def __unicode__(self): - return JOINT.join((self.name, unicode(self.department))) + return settings.JOINT.join((self.name, unicode(self.department))) class Canton(models.Model): name = models.CharField(u"Nom", max_length=30) arrondissement = models.ForeignKey(Arrondissement, verbose_name=u"Arrondissement") def __unicode__(self): - return JOINT.join((self.name, unicode(self.arrondissement))) + return settings.JOINT.join((self.name, unicode(self.arrondissement))) class Town(models.Model): name = models.CharField(_(u"Name"), max_length=100) diff --git a/ishtar_common/templates/search.html b/ishtar_common/templates/ishtar/wizard/search.html index 1b5829820..7ed214a48 100644 --- a/ishtar_common/templates/search.html +++ b/ishtar_common/templates/ishtar/wizard/search.html @@ -2,26 +2,27 @@ {% load i18n %} {% load range %} {% block extra_head %} -{{form.media}} +{{wizard.form.media}} {% endblock %} {% block content %} +<h2>{{wizard_label}}</h2> <ul id='form_path'> <li class='current'>» <a href='#'>{{form.form_label}}</a></li> </ul> <div class='form'> -{% if form.forms %} +{% if wizard.form.forms %} <div class='top_button'><input type="submit" id="submit_form" value="{% trans "Validate" %}"/></div> <table class='formset'> - {%if form.non_form_errors%}<tr class='error'><th colspan='2'>{{form.non_form_errors}}</th></tr>{%endif%} - {{ form.management_form }} - {% for formsetform in form.forms %} + {%if wizard.form.non_form_errors%}<tr class='error'><th colspan='2'>{{wizard.form.non_form_errors}}</th></tr>{%endif%} + {{ wizard.form.management_form }} + {% for formsetform in wizard.form.forms %} {{ formsetform.as_table }} {% endfor %} <tr class='modify'><td colspan="2"><button name="formset_modify" value="{{form_step}}">{% trans "Add/Modify" %}</button></td></tr></li> </table> {% else %} <table> - {{ form.as_table }} + {{ wizard.form.as_table }} </table> {% endif %} </div> diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index 40ef4a1d2..7f82ade2f 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -57,7 +57,7 @@ urlpatterns += patterns('ishtar_common.views', 'new_person', name='new-person'), url(r'autocomplete-person/([0-9_]+)?$', 'autocomplete_person', name='autocomplete-person'), - url(r'autocomplete-town/$', 'autocomplete_town', + url(r'autocomplete-town/?$', 'autocomplete_town', name='autocomplete-town'), url(r'new-author/(?P<parent_name>.+)?/$', 'new_author', name='new-author'), diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py new file mode 100644 index 000000000..7895d98e3 --- /dev/null +++ b/ishtar_common/wizards.py @@ -0,0 +1,878 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> + +# 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 <http://www.gnu.org/licenses/>. + +# See the file COPYING for details. + +import datetime + +from django.conf import settings +from django.contrib.formtools.wizard.views import NamedUrlWizardView +from django.core.exceptions import ObjectDoesNotExist +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.utils.translation import ugettext_lazy as _ + +import models + +class Wizard(NamedUrlWizardView): + model = None + label = '' + modification = None # True when the wizard modify an item + storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage' + + @staticmethod + def _check_right(step, condition=True): + '''Return a method to check the right for a specific step''' + """ + def check_right(self, request, storage): + cond = condition + if callable(condition): + cond = condition(self, request, storage) + if not cond: + return False + person_type = request.user.ishtaruser.person.person_type + if person_type.txt_idx == 'administrator': + return True + if person_type.rights.filter(url_name=step).count(): + return True""" + def check_right(self): + cond = condition + if callable(condition): + cond = condition(self) + if not cond: + return False + if not hasattr(self.request.user, 'ishtaruser'): + return False + person_type = self.request.user.ishtaruser.person.person_type + if person_type.txt_idx == 'administrator': + return True + if person_type.rights.filter(url_name=step).count(): + return True + return check_right + + def __init__(self, *args, **kwargs): + """Check right for each step of the wizard""" + print "2" + super(Wizard, self).__init__(*args, **kwargs) + for form_key in self.form_list.keys()[:-1]: + condition = True + if form_key in self.condition_dict: + condition = self.condition_dict.get(form_key, True) + cond = self._check_right(form_key, condition) + self.condition_dict[form_key] = cond + """ + for form_key in self.form_list.keys()[:-1]: + condition = True + if form_key in self.condition_list: + condition = self.condition_list.get(form_key, True) + cond = self._check_right(form_key, condition) + self.condition_list[form_key] = cond""" + + def get_prefix(self, *args, **kwargs): + """As the class name can interfere when reused prefix with the url_name + """ + print "3" + return self.url_name + super(Wizard, self).get_prefix(*args, + **kwargs) + + def get_wizard_name(self): + """As the class name can interfere when reused, use the url_name""" + print "4" + return self.url_name + + def get_template_names(self): + print "5" + templates = ['ishtar/wizard/default_wizard.html'] + current_step = self.steps.current + if current_step == self.steps.last: + templates = ['ishtar/wizard/confirm_wizard.html'] + templates + return templates + + def get_context_data(self, form, **kwargs): + """Add previous, next and current steps to manage the wizard path""" + print "6" + context = super(Wizard, self).get_context_data(form) + self.request.session['CURRENT_ACTION'] = self.get_wizard_name() + step = self.steps.first + current_step = self.steps.current + context.update({'current_step':self.form_list[current_step], + 'wizard_label':self.label}) + if step == current_step: + return context + previous_steps, next_steps, previous_step_counter = [], [], 0 + while step: + if step == current_step \ + or (previous_steps and + previous_steps[-1] == self.form_list[step]): + break + previous_steps.append(self.form_list[step]) + previous_step_counter += 1 + if previous_step_counter >= len(self.steps): + break + step = self.steps.all[previous_step_counter] + context.update({'previous_steps':previous_steps, + 'previous_step_counter':previous_step_counter}) + storage = self.storage + # if modification: show the next steps + if self.modification: + next_step = step + while next_step: + # check if the form is initialized otherwise initialize it + if not storage.get_step_data(next_step): + values = self.get_form_initial(next_step) + prefixed_values = {} + if not isinstance(values, list): + for key in values: + form_key = next_step + '-' + key + prefixed_values[form_key] = values[key] + else: + for formset_idx, v in enumerate(values): + prefix = u"-%d-" % formset_idx + for key in v: + form_key = next_step + prefix + key + prefixed_values[form_key] = v[key] + storage.set_step_data(next_step, prefixed_values) + if step != next_step: # if not current step + next_steps.append(self.form_list[next_step]) + next_step = self.get_next_step(next_step) + context.update({'next_steps':next_steps}) + # not last step: validation + if current_step != self.steps.last: + return context + final_form_list = [] + for form_key in self.get_form_list().keys(): + form_obj = self.get_form(step=form_key, + data=self.storage.get_step_data(form_key), + files=self.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""" + print "7" + 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 hasattr(base_form, 'associated_labels') \ + and key in base_form.associated_labels: + lbl = base_form.associated_labels[key] + if not lbl: + continue + value = cleaned_data[key] + if not value and value != False: + continue + if type(value) == bool: + if value == True: + value = _(u"Yes") + elif value == False: + value = _(u"No") + elif key in associated_models: + values = [] + if "," in unicode(value): + values = unicode(value).split(",") + else: + values = [value] + rendered_values = [] + for val in values: + item = associated_models[key].objects.get(pk=val) + if hasattr(item, 'short_label'): + value = item.short_label() + else: + value = unicode(item) + rendered_values.append(value) + value = u" ; ".join(rendered_values) + form_datas.append((lbl, value, '')) + if form_datas: + datas.append((form.form_label, form_datas)) + return datas + + def get_extra_model(self, dct, form_list): + print "8" + dct['history_modifier'] = self.request.user + return dct + + def done(self, form_list, return_object=False, **kwargs): + """Save to the model""" + print "9" + dct, m2m, whole_associated_models = {}, [], [] + for form in form_list: + if not form.is_valid(): + return self.render(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] + if hasattr(frm, 'base_model') and frm.base_model: + whole_associated_models.append(frm.base_model) + else: + whole_associated_models += associated_models.keys() + 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: + model = associated_models[key] + if isinstance(value, unicode) \ + or isinstance(value, str) and "," in value: + value = value.split(",") + if isinstance(value, list) \ + or isinstance(value, tuple): + value = [model.objects.get(pk=val) + for val in value if val] + if len(value) == 1: + value = value[0] + else: + value = model.objects.get(pk=value) + else: + value = None + dct[key] = value + return self.save_model(dct, m2m, whole_associated_models, form_list, + return_object) + + def get_saved_model(self): + """Permit a distinguo when saved model is not the base selected model""" + print "10" + return self.model + + def get_current_saved_object(self): + """Permit a distinguo when saved model is not the base selected model""" + print "11" + return self.get_current_object() + + def save_model(self, dct, m2m, whole_associated_models, form_list, + return_object): + print "12" + dct = self.get_extra_model(dct, form_list) + obj = self.get_current_saved_object() + # 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]) + try: + obj.full_clean() + except forms.ValidationError, msg: + return self.render(form_list[-1]) + for dependant_item in other_objs: + c_item = getattr(obj, dependant_item) + # manage ManyToMany if only one associated + if hasattr(c_item, "all"): + c_items = c_item.all() + if len(c_items) != 1: + continue + c_item = c_items[0] + 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 issubclass(model, models.BaseHistorizedItem): + c_dct['history_modifier'] = self.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) + try: + obj.full_clean() + except forms.ValidationError, msg: + return self.render(form_list[-1]) + obj.save() + for k in adds: + getattr(obj, k).add(adds[k]) + # necessary to manage interaction between models like + # material_index management for baseitems + obj.save() + m2m_items = {} + for model in whole_associated_models: + getattr(obj, model+'s').clear() + for key, value in m2m: + if key not in m2m_items: + 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: + model = getattr(obj, key+'s').model + if issubclass(model, models.BaseHistorizedItem): + value['history_modifier'] = self.request.user + value = model.objects.create(**value) + value.save() + getattr(obj, key+'s').add(value) + # necessary to manage interaction between models like + # material_index management for baseitems + obj.save() + res = render_to_response('wizard_done.html', {}, + context_instance=RequestContext(self.request)) + return return_object and (obj, res) or res + + def get_deleted(self, keys): + """Get the deleted and non-deleted items in formsets""" + print "13" + not_to_delete, to_delete = set(), set() + for key in keys: + items = key.split('-') + if len(items) < 2 or items[-2] in to_delete: + continue + idx = items[-2] + try: + int(idx) + except: + continue + if items[-1] == u'DELETE': + to_delete.add(idx) + if idx in not_to_delete: + not_to_delete.remove(idx) + elif idx not in not_to_delete: + not_to_delete.add(idx) + return (to_delete, not_to_delete) + + def get_form(self, step=None, data=None, files=None): + """Manage formset""" + print "14" + request = self.request + storage = self.storage + if data: + data = data.copy() + if not step: + step = self.steps.current + form = self.get_form_list()[step] + if hasattr(form, 'management_form'): + # manage deletion + to_delete, not_to_delete = self.get_deleted(data.keys()) + # raz deleted fields + for key in data.keys(): + items = key.split('-') + if len(items) < 2 or items[-2] not in to_delete: + continue + data.pop(key) + if to_delete: + # reorganize + for idx, number in enumerate(sorted(not_to_delete)): + idx = unicode(idx) + if idx == number: + continue + for key in data.keys(): + items = key.split('-') + if len(items) > 2 and number == items[-2]: + items[-2] = unicode(idx) + k = u'-'.join(items) + data[k] = data.pop(key)[0] + # get a form key + base_key = form.form.base_fields.keys()[0] + init = self.get_form_initial(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(step, data, files) + return form + + def render_next_step(self, form, **kwargs): + """ + Manage: + - modify or delete button in formset: next step = current step + - validate and end: nextstep = last step + """ + print "15" + request = self.request + 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(form) + elif request.POST.has_key('validate_and_end') \ + and request.POST['validate_and_end']: + last_step = self.steps.last + new_form = self.get_form(last_step, + data=self.storage.get_step_data(last_step), + files=self.storage.get_step_files(last_step)) + self.storage.current_step = last_step + return self.render(new_form) + return super(Wizard, self).render_next_step(form, **kwargs) + + def post(self, *args, **kwargs): + """Convert numerical step number to step name""" + print "16" + request = self.request + post_data = request.POST.copy() + if request.POST.has_key('form_prev_step'): + try: + step_number = int(request.POST['form_prev_step']) + post_data['wizard_goto_step'] = self.get_form_list().keys( + )[step_number] + except ValueError: + pass + request.POST = post_data + return super(Wizard, self).post(*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 + """ + print "17" + 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""" + print "18" + 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): + """Get the current object for an instancied wizard""" + print "19" + current_obj = None + main_form_key = 'selec-' + self.url_name + try: + idx = self.session_get_value(self.request, self.storage, + main_form_key, 'pk') + if type(idx) in (tuple, list): + idx = idx[0] + idx = int(idx) + current_obj = self.model.objects.get(pk=idx) + except(TypeError, ValueError, ObjectDoesNotExist): + pass + return current_obj + + def get_form_initial(self, step): + print "20" + current_obj = self.get_current_object() + current_step = self.steps.current + request = self.request + 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) + self.storage.reset() + 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, 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(step) + + def get_instanced_init(self, obj, step=None): + """Get initial data from an init""" + print "21" + current_step = step or self.steps.current + 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 = self.storage.prefix.split('_') + if len(prefixes) > 1 and prefixes[-2].startswith(obj_name): + obj_name = prefixes[-2] + self.request.session[obj_name] = unicode(obj.pk) + initial = {} + if self.request.POST or \ + (step in self.request.session[self.storage.prefix] and\ + self.request.session[self.storage.prefix]['step_data'][step]): + return {} + 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 = [] + if hasattr(c_form.form, 'base_model'): + key = c_form.form.base_model + 's' + else: + key = current_step.split('-')[0] + if not hasattr(obj, key): + return initial + keys = c_form.form.base_fields.keys() + for child_obj in getattr(obj, key).order_by('pk').all(): + if not keys: + break + vals = {} + 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 + +class SearchWizard(NamedUrlWizardView): + model = None + label = '' + modification = None # True when the wizard modify an item + storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage' + + def get_wizard_name(self): + """ + As the class name can interfere when reused, use the url_name + """ + return self.url_name + + def get_prefix(self, *args, **kwargs): + """As the class name can interfere when reused prefix with the url_name + """ + return self.url_name + super(SearchWizard, self).get_prefix(*args, + **kwargs) + + def get_template_names(self): + templates = ['ishtar/wizard/search.html'] + return templates + +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_delete_done.html', {}, + context_instance=RequestContext(request)) + +class ClosingWizard(Wizard): + # "close" an item + # to be define in the overloaded class + model = None + fields = [] + + def get_formated_datas(self, forms): + datas = super(ClosingWizard, 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) + for form in form_list: + if form.is_valid(): + if 'end_date' in form.cleaned_data and hasattr(obj, 'end_date'): + obj.end_date = form.cleaned_data['end_date'] + obj.save() + return render_to_response('wizard_closing_done.html', {}, + context_instance=RequestContext(request)) + +class PersonWizard(Wizard): + model = models.Person + +class PersonModifWizard(PersonWizard): + modification = True + +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, form_list, **kwargs): + """ + Save the account + """ + dct = {} + for form in form_list: + if not form.is_valid(): + return self.render(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() + if not person: + return self.render(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(self.request)) + return res + + def get_form(self, step=None, data=None, files=None): + """ + Display the "Send email" field if necessary + """ + form = super(AccountWizard, self).get_form(step, data, files) + if not hasattr(form, 'is_hidden'): + return form + if self.session_get_value(self.request, self.storage, + 'account-account_management', 'hidden_password'): + form.is_hidden = False + return form + +class SourceWizard(Wizard): + model = None + def get_extra_model(self, dct, request, storage, form_list): + dct = super(SourceWizard, self).get_extra_model(dct, request, storage, + form_list) + if 'history_modifier' in dct: + dct.pop('history_modifier') + return dct |