diff options
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/forms.py | 63 | ||||
-rw-r--r-- | ishtar_common/forms_common.py | 268 | ||||
-rw-r--r-- | ishtar_common/templates/blocks/JQueryAutocomplete.js | 21 | ||||
-rw-r--r-- | ishtar_common/templatetags/replace_underscore.py | 1 | ||||
-rw-r--r-- | ishtar_common/urls.py | 188 | ||||
-rw-r--r-- | ishtar_common/views.py | 496 | ||||
-rw-r--r-- | ishtar_common/widgets.py | 178 | ||||
-rw-r--r-- | ishtar_common/wizards.py | 168 |
8 files changed, 779 insertions, 604 deletions
diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py index e6f21ae5b..a5abdddcd 100644 --- a/ishtar_common/forms.py +++ b/ishtar_common/forms.py @@ -22,27 +22,18 @@ Forms definition """ import datetime import re -from itertools import groupby from django import forms -from django.conf import settings -from django.contrib.auth.models import User -from django.contrib.sites.models import Site from django.core.urlresolvers import reverse from django.core import validators -from django.core.exceptions import ObjectDoesNotExist -from django.core.mail import send_mail -from django.db.models import Max -from django.forms.formsets import formset_factory, BaseFormSet, \ - DELETION_FIELD_NAME -from django.shortcuts import render_to_response -from django.template import Context, RequestContext, loader +from django.forms.formsets import BaseFormSet, DELETION_FIELD_NAME from django.utils import formats from django.utils.functional import lazy from django.utils.translation import ugettext_lazy as _ # from formwizard.forms import NamedUrlSessionFormWizard + class NamedUrlSessionFormWizard(forms.Form): def __init__(self, form_list, condition_list={}, url_name=''): self.form_list = dict(form_list) @@ -59,8 +50,11 @@ import widgets reverse_lazy = lazy(reverse, unicode) 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') +name_validator = validators.RegexValidator( + regexp_name, + _(u"Enter a valid name consisting of letters, spaces and hyphens."), + 'invalid') + class FloatField(forms.FloatField): """ @@ -71,14 +65,17 @@ class FloatField(forms.FloatField): value = value.replace(',', '.').replace('%', '') return super(FloatField, self).clean(value) + class FinalForm(forms.Form): final = True form_label = _(u"Confirm") + class FinalDeleteForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Are you sure you want to delete?") + class FormSet(BaseFormSet): def check_duplicate(self, key_names, error_msg=""): """Check for duplicate items in the formset""" @@ -91,7 +88,8 @@ class FormSet(BaseFormSet): form = self.forms[i] if not form.is_valid(): continue - item = [key_name in form.cleaned_data and form.cleaned_data[key_name] + 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 @@ -104,6 +102,7 @@ class FormSet(BaseFormSet): form.fields[DELETION_FIELD_NAME].label = '' form.fields[DELETION_FIELD_NAME].widget = widgets.DeleteWidget() + class TableSelect(forms.Form): def __init__(self, *args, **kwargs): super(TableSelect, self).__init__(*args, **kwargs) @@ -113,19 +112,23 @@ class TableSelect(forms.Form): def get_input_ids(self): return self.fields.keys() + def get_now(): format = formats.get_format('DATE_INPUT_FORMATS')[0] value = datetime.datetime.now().strftime(format) return value + class ClosingDateFormSelection(forms.Form): form_label = _("Closing date") end_date = forms.DateField(label=_(u"Closing date"), widget=widgets.JQueryDate) -def get_form_selection(class_name, label, key, model, base_form, get_url, - not_selected_error=_(u"You should select an item."), new=False, - new_message=_(u"Add a new item")): + +def get_form_selection( + class_name, label, key, model, base_form, get_url, + not_selected_error=_(u"You should select an item."), new=False, + new_message=_(u"Add a new item")): """ Generate a class selection form class_name -- name of the class @@ -137,15 +140,17 @@ def get_form_selection(class_name, label, key, model, base_form, get_url, new -- can add new items new_message -- message of link to add new items """ - attrs = {'_main_key':key, - '_not_selected_error':not_selected_error, - 'form_label':label, - 'associated_models':{key:model}, - 'currents':{key:model},} - attrs[key] = forms.IntegerField(label="", required=False, + attrs = {'_main_key': key, + '_not_selected_error': not_selected_error, + 'form_label': label, + 'associated_models': {key: model}, + 'currents': {key: model}} + attrs[key] = forms.IntegerField( + label="", required=False, validators=[models.valid_id(model)], widget=widgets.JQueryJqGrid(reverse_lazy(get_url), base_form, model, new=new, new_message=new_message)) + def clean(self): cleaned_data = self.cleaned_data if self._main_key not in cleaned_data \ @@ -155,19 +160,21 @@ def get_form_selection(class_name, label, key, model, base_form, get_url, attrs['clean'] = clean return type(class_name, (forms.Form,), attrs) + class DocumentGenerationForm(forms.Form): """ Form to generate document by choosing the template """ - _associated_model = None # ex: AdministrativeAct - _associated_object_name = '' # ex: 'archaeological_operations.models.AdministrativeAct' + _associated_model = None # ex: AdministrativeAct + # ex: 'archaeological_operations.models.AdministrativeAct' + _associated_object_name = '' document_template = forms.ChoiceField(label=_("Template"), choices=[]) def __init__(self, *args, **kwargs): super(DocumentGenerationForm, self).__init__(*args, **kwargs) self.fields['document_template'].choices = \ - models.DocumentTemplate.get_tuples( - dct={'associated_object_name':self._associated_object_name}) + models.DocumentTemplate.get_tuples( + dct={'associated_object_name': self._associated_object_name}) def save(self, object_pk): try: @@ -176,7 +183,7 @@ class DocumentGenerationForm(forms.Form): return try: template = models.DocumentTemplate.objects.get( - pk=self.cleaned_data.get('document_template')) + pk=self.cleaned_data.get('document_template')) except models.DocumentTemplate.DoesNotExist: return return template.publish(c_object) diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 509b9fbc5..b2aa565bb 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -20,19 +20,14 @@ """ Administrative forms definitions: manage accounts and persons """ -import datetime from django import forms from django.conf import settings from django.contrib.auth.models import User -from django.contrib.sites.models import Site from django.core import validators -from django.core.exceptions import ObjectDoesNotExist, ValidationError -from django.core.mail import send_mail -from django.forms.formsets import formset_factory, DELETION_FIELD_NAME +from django.core.exceptions import ObjectDoesNotExist +from django.forms.formsets import formset_factory from django.forms.models import BaseModelFormSet -from django.template import Context, RequestContext, loader -from django.shortcuts import render_to_response from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ @@ -40,27 +35,33 @@ import models import widgets from forms import FinalForm, FormSet, reverse_lazy, name_validator, TableSelect + def get_town_field(label=_(u"Town"), required=True): - help_text = _(u"<p>Type name, department code and/or postal code of the " - u"town you would like to select. The search is insensitive to case.</p>\n" - u"<p>Only the first twenty results are displayed but specifying the " - u"department code is generally sufficient to get the appropriate result.</p>" - u"\n<p class='example'>For instance type \"saint denis 93\" for getting " - u"the french town Saint-Denis in the Seine-Saint-Denis department.</p>") + help_text = _( + u"<p>Type name, department code and/or postal code of the " + u"town you would like to select. The search is insensitive to case." + u"</p>\n<p>Only the first twenty results are displayed but specifying " + u"the department code is generally sufficient to get the appropriate " + u"result.</p>\n<p class='example'>For instance type \"saint denis 93\"" + u" for getting the french town Saint-Denis in the Seine-Saint-Denis " + u"department.</p>") # !FIXME hard_link, reverse_lazy doen't seem to work with formsets return forms.IntegerField( - widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ - 'autocomplete-town', associated_model=models.Town), - validators=[models.valid_id(models.Town)], label=label, - help_text=mark_safe(help_text), required=required) + widget=widgets.JQueryAutoComplete( + "/" + settings.URL_PATH + 'autocomplete-town', + associated_model=models.Town), + validators=[models.valid_id(models.Town)], label=label, + help_text=mark_safe(help_text), required=required) + def get_advanced_town_field(label=_(u"Town"), required=True): # !FIXME hard_link, reverse_lazy doen't seem to work with formsets return forms.IntegerField( - widget=widgets.JQueryTown("/" + settings.URL_PATH + \ - 'autocomplete-advanced-town'), - validators=[models.valid_id(models.Town)], label=label, - required=required) + widget=widgets.JQueryTown( + "/" + settings.URL_PATH + 'autocomplete-advanced-town'), + validators=[models.valid_id(models.Town)], label=label, + required=required) + def get_person_field(label=_(u"Person"), required=True, person_types=[]): # !FIXME hard_link, reverse_lazy doen't seem to work with formsets @@ -69,12 +70,13 @@ def get_person_field(label=_(u"Person"), required=True, person_types=[]): if person_types: person_types = [ unicode(models.PersonType.objects.get(txt_idx=person_type).pk) - for person_type in person_types] + for person_type in person_types] url += u"/" + u'_'.join(person_types) widget = widgets.JQueryAutoComplete(url, associated_model=models.Person) return forms.IntegerField(widget=widget, label=label, required=required, validators=[models.valid_id(models.Person)]) + class NewItemForm(forms.Form): def __init__(self, *args, **kwargs): self.limits = {} @@ -97,6 +99,7 @@ class NewItemForm(forms.Form): if len(new_choices) == 1: self.fields[key].initial = [new_choices[0][0]] + class NewImportForm(forms.ModelForm): class Meta: model = models.Import @@ -106,12 +109,13 @@ class NewImportForm(forms.ModelForm): self.instance.user = user return super(NewImportForm, self).save(commit) + class TargetKeyForm(forms.ModelForm): class Meta: model = models.TargetKey fields = ('target', 'key', 'value') widgets = { - 'key': forms.TextInput(attrs={'readonly':'readonly'}), + 'key': forms.TextInput(attrs={'readonly': 'readonly'}), 'value': forms.Select(), } @@ -121,7 +125,8 @@ class TargetKeyForm(forms.ModelForm): if instance and instance.pk: self.fields['target'].widget.attrs['readonly'] = True self.fields['key'].widget.attrs['readonly'] = True - self.fields['value'].widget.choices = list(instance.target.get_choices()) + self.fields['value'].widget.choices = list( + instance.target.get_choices()) self.fields['key'].required = False self.fields['value'].required = False @@ -146,11 +151,12 @@ class TargetKeyForm(forms.ModelForm): self.associated_import = None self.instance.save() + class OrganizationForm(NewItemForm): form_label = _(u"Organization") - associated_models = {'organization_type':models.OrganizationType} - name = forms.CharField(label=_(u"Name"), max_length=300, - validators=[name_validator]) + associated_models = {'organization_type': models.OrganizationType} + name = forms.CharField( + label=_(u"Name"), max_length=300, validators=[name_validator]) organization_type = forms.ChoiceField(label=_(u"Organization type"), choices=[]) address = forms.CharField(label=_(u"Address"), widget=forms.Textarea, @@ -170,20 +176,21 @@ class OrganizationForm(NewItemForm): def __init__(self, *args, **kwargs): super(OrganizationForm, self).__init__(*args, **kwargs) self.fields['organization_type'].choices = \ - models.OrganizationType.get_types() + models.OrganizationType.get_types() self.fields['organization_type'].help_text = \ - models.OrganizationType.get_help() + models.OrganizationType.get_help() self.limit_fields() def save(self, user): dct = self.cleaned_data dct['history_modifier'] = user dct['organization_type'] = models.OrganizationType.objects.get( - pk=dct['organization_type']) + pk=dct['organization_type']) new_item = models.Organization(**dct) new_item.save() return new_item + class OrganizationSelect(TableSelect): name = forms.CharField(label=_(u"Name"), max_length=300) organization_type = forms.ChoiceField(label=_(u"Type"), choices=[]) @@ -191,56 +198,65 @@ class OrganizationSelect(TableSelect): def __init__(self, *args, **kwargs): super(OrganizationSelect, self).__init__(*args, **kwargs) self.fields['organization_type'].choices = \ - models.OrganizationType.get_types() + models.OrganizationType.get_types() + class OrganizationFormSelection(forms.Form): form_label = _(u"Organization search") - associated_models = {'pk':models.Organization} - currents = {'pk':models.Organization} - pk = forms.IntegerField(label="", - widget=widgets.JQueryJqGrid(reverse_lazy('get-organization'), - OrganizationSelect, models.Organization), - validators=[models.valid_id(models.Organization)]) + associated_models = {'pk': models.Organization} + currents = {'pk': models.Organization} + pk = forms.IntegerField( + label="", + widget=widgets.JQueryJqGrid(reverse_lazy('get-organization'), + OrganizationSelect, models.Organization), + validators=[models.valid_id(models.Organization)]) + class BaseOrganizationForm(forms.ModelForm): form_prefix = "orga" + class Meta: model = models.Organization fields = ['name', 'organization_type', 'address', 'address_complement', - 'town', 'postal_code',] + 'town', 'postal_code'] + class PersonSelect(TableSelect): name = forms.CharField(label=_(u"Name"), max_length=30) surname = forms.CharField(label=_(u"Surname"), max_length=20) email = forms.CharField(label=_(u"Email"), max_length=75) person_types = forms.ChoiceField(label=_(u"Type"), choices=[]) - attached_to = forms.IntegerField(label=_("Organization"), - widget=widgets.JQueryAutoComplete( - reverse_lazy('autocomplete-organization'), - associated_model=models.Organization), - validators=[models.valid_id(models.Organization)]) + attached_to = forms.IntegerField( + label=_("Organization"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-organization'), + associated_model=models.Organization), + validators=[models.valid_id(models.Organization)]) def __init__(self, *args, **kwargs): super(PersonSelect, self).__init__(*args, **kwargs) self.fields['person_types'].choices = models.PersonType.get_types() + class PersonFormSelection(forms.Form): form_label = _(u"Person search") - associated_models = {'pk':models.Person} - currents = {'pk':models.Person} - pk = forms.IntegerField(label="", - widget=widgets.JQueryJqGrid(reverse_lazy('get-person'), - PersonSelect, models.Person), - validators=[models.valid_id(models.Person)]) + associated_models = {'pk': models.Person} + currents = {'pk': models.Person} + pk = forms.IntegerField( + label="", + widget=widgets.JQueryJqGrid(reverse_lazy('get-person'), + PersonSelect, models.Person), + validators=[models.valid_id(models.Person)]) + class SimplePersonForm(NewItemForm): form_label = _("Identity") - associated_models = {'attached_to':models.Organization} + associated_models = {'attached_to': models.Organization} 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]) + validators=[name_validator]) address = forms.CharField(label=_(u"Address"), widget=forms.Textarea, required=False) address_complement = forms.CharField(label=_(u"Address complement"), @@ -254,28 +270,31 @@ class SimplePersonForm(NewItemForm): phone = forms.CharField(label=_(u"Phone"), max_length=18, required=False) mobile_phone = forms.CharField(label=_(u"Town"), max_length=18, required=False) - attached_to = forms.IntegerField(label=_("Current organization"), + attached_to = forms.IntegerField( + label=_("Current organization"), widget=widgets.JQueryAutoComplete( - reverse_lazy('autocomplete-organization'), - associated_model=models.Organization, new=True), + reverse_lazy('autocomplete-organization'), + associated_model=models.Organization, new=True), validators=[models.valid_id(models.Organization)], required=False) + class BasePersonForm(forms.ModelForm): class Meta: model = models.Person fields = ['title', 'name', 'surname', 'address', 'address_complement', 'town', 'postal_code'] + class BaseOrganizationPersonForm(forms.ModelForm): class Meta: model = models.Person - fields = ['attached_to', 'title', 'name', 'surname',] - widgets = {'attached_to':widgets.JQueryPersonOrganization( - reverse_lazy('autocomplete-organization'), - reverse_lazy('organization_create'), - model=models.Organization, - attrs={'hidden':True}, - new=True), + fields = ['attached_to', 'title', 'name', 'surname'] + widgets = {'attached_to': widgets.JQueryPersonOrganization( + reverse_lazy('autocomplete-organization'), + reverse_lazy('organization_create'), + model=models.Organization, + attrs={'hidden': True}, + new=True), } def __init__(self, *args, **kwargs): @@ -293,14 +312,16 @@ class BaseOrganizationPersonForm(forms.ModelForm): person.save() return person + class PersonForm(SimplePersonForm): - person_types = forms.MultipleChoiceField(label=_("Person type"), - choices=[], widget=forms.CheckboxSelectMultiple) + person_types = forms.MultipleChoiceField( + label=_("Person type"), choices=[], + widget=forms.CheckboxSelectMultiple) def __init__(self, *args, **kwargs): super(PersonForm, self).__init__(*args, **kwargs) self.fields['person_types'].choices = models.PersonType.get_types( - empty_first=False) + empty_first=False) self.fields['person_types'].help_text = models.PersonType.get_help() self.limit_fields() @@ -309,48 +330,58 @@ class PersonForm(SimplePersonForm): dct['history_modifier'] = user if 'attached_to' in dct and dct['attached_to']: dct['attached_to'] = models.Organization.objects.get( - pk=dct['attached_to']) + pk=dct['attached_to']) person_types = dct.pop('person_types') new_item = models.Person.objects.create(**dct) for pt in person_types: new_item.person_types.add(pt) return new_item + +class NoOrgaPersonForm(PersonForm): + def __init__(self, *args, **kwargs): + super(NoOrgaPersonForm, self).__init__(*args, **kwargs) + self.fields.pop('attached_to') + + class PersonTypeForm(forms.Form): form_label = _("Person type") base_model = 'person_type' - associated_models = {'person_type':models.PersonType} - person_type = forms.MultipleChoiceField(label=_("Person type"), - choices=[], widget=forms.CheckboxSelectMultiple) + associated_models = {'person_type': models.PersonType} + person_type = forms.MultipleChoiceField( + label=_("Person type"), choices=[], + widget=forms.CheckboxSelectMultiple) def __init__(self, *args, **kwargs): super(PersonTypeForm, self).__init__(*args, **kwargs) self.fields['person_type'].choices = models.PersonType.get_types( - empty_first=False) + empty_first=False) self.fields['person_type'].help_text = models.PersonType.get_help() + class AccountForm(forms.Form): form_label = _("Account") - associated_models = {'pk':models.Person} - currents = {'pk':models.Person} - pk = forms.IntegerField(label=u"", widget=forms.HiddenInput, required=False) + associated_models = {'pk': models.Person} + currents = {'pk': models.Person} + pk = forms.IntegerField(label=u"", 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 = 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) + 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}) + kwargs['initial'].update({'username': account.username, + 'email': account.email}) except ObjectDoesNotExist: pass return super(AccountForm, self).__init__(*args, **kwargs) @@ -358,23 +389,25 @@ class AccountForm(forms.Form): def clean(self): cleaned_data = self.cleaned_data password = cleaned_data.get("hidden_password") - if password and password != cleaned_data.get("hidden_password_confirm"): + 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 "\ + raise forms.ValidationError(_(u"You must provide a correct " u"password.")) # check username unicity - usernames = models.IshtarUser.objects.filter(username= - cleaned_data.get('username')) + usernames = models.IshtarUser.objects.filter( + username=cleaned_data.get('username')) if cleaned_data.get('pk'): usernames.exclude(pk=cleaned_data.get('pk')) if usernames.count(): raise forms.ValidationError(_(u"This username already exists.")) return cleaned_data + class FinalAccountForm(forms.Form): final = True form_label = _("Confirm") @@ -385,12 +418,14 @@ class FinalAccountForm(forms.Form): self.is_hidden = True return super(FinalAccountForm, self).__init__(*args, **kwargs) + class TownForm(forms.Form): form_label = _("Towns") base_model = 'town' - associated_models = {'town':models.Town} + associated_models = {'town': models.Town} town = get_town_field(required=False) + class TownFormSet(FormSet): def clean(self): """Checks that no towns are duplicated.""" @@ -400,9 +435,11 @@ class TownFormSet(FormSet): TownFormset = formset_factory(TownForm, can_delete=True, formset=TownFormSet) TownFormset.form_label = _("Towns") + class MergeFormSet(BaseModelFormSet): from_key = '' to_key = '' + def __init__(self, *args, **kwargs): self._cached_list = [] super(MergeFormSet, self).__init__(*args, **kwargs) @@ -426,11 +463,11 @@ class MergeFormSet(BaseModelFormSet): if self.is_bound and i < self.initial_form_count(): # Import goes here instead of module-level because importing # django.db has side effects. - from django.db import connections + # from django.db import connections pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name) pk = self.data[pk_key] - pk_field = self.model._meta.pk - """pk = pk_field.get_db_prep_lookup('exact', pk, + """pk_field = self.model._meta.pk + pk = pk_field.get_db_prep_lookup('exact', pk, connection=connections[self.get_queryset().db])""" pk = self.get_restricted_queryset()[i].pk if isinstance(pk, list): @@ -441,7 +478,8 @@ class MergeFormSet(BaseModelFormSet): if i >= self.initial_form_count() and self.initial_extra: # Set initial values for extra forms try: - kwargs['initial'] = self.initial_extra[i-self.initial_form_count()] + kwargs['initial'] = \ + self.initial_extra[i-self.initial_form_count()] except IndexError: pass return super(BaseModelFormSet, self)._construct_form(i, **kwargs) @@ -464,8 +502,10 @@ class MergeFormSet(BaseModelFormSet): self._cached_list = res return res + class MergeForm(forms.ModelForm): - id = forms.IntegerField(label=u"", widget=forms.HiddenInput, required=False) + id = forms.IntegerField( + label=u"", widget=forms.HiddenInput, required=False) a_is_duplicate_b = forms.BooleanField(required=False) b_is_duplicate_a = forms.BooleanField(required=False) not_duplicate = forms.BooleanField(required=False) @@ -492,13 +532,14 @@ class MergeForm(forms.ModelForm): else: return try: - reverse = self.instance.__class__.objects.get( - **{self.TO_KEY:from_item, - self.FROM_KEY:to_item}).delete() + self.instance.__class__.objects.get( + **{self.TO_KEY: from_item, + self.FROM_KEY: to_item}).delete() except ObjectDoesNotExist: pass self.instance.delete() + class MergePersonForm(MergeForm): class Meta: model = models.Person @@ -507,6 +548,7 @@ class MergePersonForm(MergeForm): FROM_KEY = 'from_person' TO_KEY = 'to_person' + class MergeOrganizationForm(MergeForm): class Meta: model = models.Organization @@ -521,12 +563,12 @@ class MergeOrganizationForm(MergeForm): ###################### class SourceForm(forms.Form): form_label = _(u"Documentation informations") - associated_models = {'source_type':models.SourceType} + associated_models = {'source_type': models.SourceType} title = forms.CharField(label=_(u"Title"), validators=[validators.MaxLengthValidator(200)]) source_type = forms.ChoiceField(label=_(u"Source type"), choices=[]) - associated_url = forms.URLField(required=False, - label=_(u"Numerical ressource (web address)")) + associated_url = forms.URLField( + required=False, label=_(u"Numerical ressource (web address)")) receipt_date = forms.DateField(label=_(u"Receipt date"), required=False, widget=widgets.JQueryDate) creation_date = forms.DateField(label=_(u"Creation date"), required=False, @@ -536,12 +578,14 @@ class SourceForm(forms.Form): super(SourceForm, self).__init__(*args, **kwargs) self.fields['source_type'].choices = models.SourceType.get_types() + class SourceSelect(TableSelect): authors = forms.IntegerField( - widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ - 'autocomplete-author', associated_model=models.Author), - validators=[models.valid_id(models.Author)], label=_(u"Author"), - required=False) + widget=widgets.JQueryAutoComplete( + "/" + settings.URL_PATH + 'autocomplete-author', + associated_model=models.Author), + validators=[models.valid_id(models.Author)], label=_(u"Author"), + required=False) source_type = forms.ChoiceField(label=_("Source type"), choices=[]) @@ -550,6 +594,7 @@ class SourceSelect(TableSelect): self.fields['source_type'].choices = models.SourceType.get_types() self.fields['source_type'].help_text = models.SourceType.get_help() + class SourceDeletionForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to delete this documentation?") @@ -558,14 +603,16 @@ class SourceDeletionForm(FinalForm): # Authors management # ###################### + class AuthorForm(NewItemForm): form_label = _(u"Author") - associated_models = {'person':models.Person, - 'author_type':models.AuthorType} + associated_models = {'person': models.Person, + 'author_type': models.AuthorType} person = forms.IntegerField( - widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ - 'autocomplete-person', associated_model=models.Person, new=True), - validators=[models.valid_id(models.Person)], label=_(u"Person")) + widget=widgets.JQueryAutoComplete( + "/" + settings.URL_PATH + 'autocomplete-person', + associated_model=models.Person, new=True), + validators=[models.valid_id(models.Person)], label=_(u"Person")) author_type = forms.ChoiceField(label=_(u"Author type"), choices=[]) def __init__(self, *args, **kwargs): @@ -576,7 +623,7 @@ class AuthorForm(NewItemForm): def save(self, user): dct = self.cleaned_data dct['author_type'] = models.AuthorType.objects.get( - pk=dct['author_type']) + pk=dct['author_type']) dct['person'] = models.Person.objects.get(pk=dct['person']) new_item = models.Author(**dct) new_item.save() @@ -586,11 +633,13 @@ class AuthorForm(NewItemForm): class AuthorFormSelection(forms.Form): form_label = _(u"Author selection") base_model = 'author' - associated_models = {'author':models.Author} + associated_models = {'author': models.Author} author = forms.IntegerField( - widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ - 'autocomplete-author', associated_model=models.Author, new=True), - validators=[models.valid_id(models.Author)], label=_(u"Author")) + widget=widgets.JQueryAutoComplete( + "/" + settings.URL_PATH + 'autocomplete-author', + associated_model=models.Author, new=True), + validators=[models.valid_id(models.Author)], label=_(u"Author")) + class AuthorFormSet(FormSet): def clean(self): @@ -601,4 +650,3 @@ class AuthorFormSet(FormSet): AuthorFormset = formset_factory(AuthorFormSelection, can_delete=True, formset=AuthorFormSet) AuthorFormset.form_label = _("Authors") - diff --git a/ishtar_common/templates/blocks/JQueryAutocomplete.js b/ishtar_common/templates/blocks/JQueryAutocomplete.js index eb365c38a..038acf1ba 100644 --- a/ishtar_common/templates/blocks/JQueryAutocomplete.js +++ b/ishtar_common/templates/blocks/JQueryAutocomplete.js @@ -1,8 +1,13 @@ +{% load replace_underscore %} +var base_source_{{field_id|replace_underscore}} = {{source}}; +var source_{{field_id|replace_underscore}} = base_source_{{field_id|replace_underscore}}; + $("#id_select_{{field_id}}").autocomplete({ - source: {{source}}, + source: source_{{field_id|replace_underscore}}, select: function( event, ui ) { if(ui.item){ $('#id_{{field_id}}').val(ui.item.id); + $('#id_{{field_id}}').change(); } else { $('#id_{{field_id}}').val(null); } @@ -17,3 +22,17 @@ $('#id_select_{{field_id}}').live('click', function(){ $('#id_select_{{field_id}}').val(null); }); + +{% if dynamic_limit %}{% for item_id in dynamic_limit %} +$('#{{item_id}}').change(function(){ + $("#id_select_{{field_id}}").autocomplete( "option", "source", + base_source_{{field_id|replace_underscore}} + $('#{{item_id}}').val() + + '/'); + if ($('#{{item_id}}').val()){ + $("#id_select_{{field_id}}").prop("disabled", false); + } else { + $("#id_select_{{field_id}}").prop("disabled", true); + } +}); +$('#{{item_id}}').change(); +{% endfor %}{% endif %} diff --git a/ishtar_common/templatetags/replace_underscore.py b/ishtar_common/templatetags/replace_underscore.py index 66931e6fe..d49fcf4fb 100644 --- a/ishtar_common/templatetags/replace_underscore.py +++ b/ishtar_common/templatetags/replace_underscore.py @@ -5,6 +5,7 @@ from django.template import Library register = Library() + @register.filter def replace_underscore(value): return value.replace('-', '_') diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index bce968b51..eb0fe7ac2 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -18,7 +18,7 @@ # See the file COPYING for details. from django.conf import settings -from django.conf.urls.defaults import * +from django.conf.urls.defaults import patterns, include, url from django.conf.urls.static import static from menus import menu @@ -29,53 +29,54 @@ urlpatterns, actions = [], [] # forms -urlpatterns = patterns('', - # internationalization - url(r'^i18n/', include('django.conf.urls.i18n')), - # General - url(r'person_search/(?P<step>.+)?$', - views.person_search_wizard, name='person_search'), - url(r'person_creation/(?P<step>.+)?$', - views.person_creation_wizard, name='person_creation'), - url(r'person_modification/(?P<step>.+)?$', - views.person_modification_wizard, name='person_modification'), - url(r'person_deletion/(?P<step>.+)?$', - views.person_deletion_wizard, name='person_deletion'), - url(r'^person-edit/$', - views.PersonCreate.as_view(), name='person_create'), - url(r'^person-edit/(?P<pk>\d+)$', - views.PersonEdit.as_view(), name='person_edit'), - url(r'organization_search/(?P<step>.+)?$', - views.organization_search_wizard, name='organization_search'), - url(r'organization_creation/(?P<step>.+)?$', - views.organization_creation_wizard, name='organization_creation'), - url(r'organization_modification/(?P<step>.+)?$', - views.organization_modification_wizard, - name='organization_modification'), - url(r'organization_deletion/(?P<step>.+)?$', - views.organization_deletion_wizard, name='organization_deletion'), - url(r'organization-edit/$', - views.OrganizationCreate.as_view(), name='organization_create'), - url(r'organization-edit/(?P<pk>\d+)$', - views.OrganizationEdit.as_view(), name='organization_edit'), - url(r'organization-person-edit/$', - views.OrganizationPersonCreate.as_view(), - name='organization_person_create'), - url(r'organization-person-edit/(?P<pk>\d+)$', - views.OrganizationPersonEdit.as_view(), - name='organization_person_edit'), - url(r'account_management/(?P<step>.+)?$', - views.account_management_wizard, name='account_management'), - url(r'^import-new/$', views.NewImportView.as_view(), name='new_import'), - url(r'^import-list/$', views.ImportListView.as_view(), - name='current_imports'), - url(r'^import-list-old/$', views.ImportOldListView.as_view(), - name='old_imports'), - url(r'^import-delete/(?P<pk>[0-9]+)/$', - views.ImportDeleteView.as_view(), name='import_delete'), - url(r'^import-link-unmatched/(?P<pk>[0-9]+)/$', - views.ImportLinkView.as_view(), name='import_link_unmatched'), - ) +urlpatterns = patterns( + '', + # internationalization + url(r'^i18n/', include('django.conf.urls.i18n')), + # General + url(r'person_search/(?P<step>.+)?$', + views.person_search_wizard, name='person_search'), + url(r'person_creation/(?P<step>.+)?$', + views.person_creation_wizard, name='person_creation'), + url(r'person_modification/(?P<step>.+)?$', + views.person_modification_wizard, name='person_modification'), + url(r'person_deletion/(?P<step>.+)?$', + views.person_deletion_wizard, name='person_deletion'), + url(r'^person-edit/$', + views.PersonCreate.as_view(), name='person_create'), + url(r'^person-edit/(?P<pk>\d+)$', + views.PersonEdit.as_view(), name='person_edit'), + url(r'organization_search/(?P<step>.+)?$', + views.organization_search_wizard, name='organization_search'), + url(r'organization_creation/(?P<step>.+)?$', + views.organization_creation_wizard, name='organization_creation'), + url(r'organization_modification/(?P<step>.+)?$', + views.organization_modification_wizard, + name='organization_modification'), + url(r'organization_deletion/(?P<step>.+)?$', + views.organization_deletion_wizard, name='organization_deletion'), + url(r'organization-edit/$', + views.OrganizationCreate.as_view(), name='organization_create'), + url(r'organization-edit/(?P<pk>\d+)$', + views.OrganizationEdit.as_view(), name='organization_edit'), + url(r'organization-person-edit/$', + views.OrganizationPersonCreate.as_view(), + name='organization_person_create'), + url(r'organization-person-edit/(?P<pk>\d+)$', + views.OrganizationPersonEdit.as_view(), + name='organization_person_edit'), + url(r'account_management/(?P<step>.+)?$', + views.account_management_wizard, name='account_management'), + url(r'^import-new/$', views.NewImportView.as_view(), name='new_import'), + url(r'^import-list/$', views.ImportListView.as_view(), + name='current_imports'), + url(r'^import-list-old/$', views.ImportOldListView.as_view(), + name='old_imports'), + url(r'^import-delete/(?P<pk>[0-9]+)/$', + views.ImportDeleteView.as_view(), name='import_delete'), + url(r'^import-link-unmatched/(?P<pk>[0-9]+)/$', + views.ImportLinkView.as_view(), name='import_link_unmatched'), +) for section in menu.childs: for menu_item in section.childs: if hasattr(menu_item, 'childs'): @@ -86,50 +87,57 @@ for section in menu.childs: actions = r"|".join(actions) # other views -urlpatterns += patterns('ishtar_common.views', - # General - url(r'dashboard-main/$', 'dashboard_main', - name='dashboard-main'), - url(r'dashboard-main/(?P<item_name>[a-z-]+)/$', 'dashboard_main_detail', - name='dashboard-main-detail'), - url(r'update-current-item/$', 'update_current_item', - name='update-current-item'), - url(r'new-person/(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$', - 'new_person', name='new-person'), - url(r'autocomplete-person(?:/([0-9_]+))?/(user)?$', 'autocomplete_person', - name='autocomplete-person'), - url(r'get-person/(?P<type>.+)?$', 'get_person', - name='get-person'), - url(r'show-person(?:/(?P<pk>.+))?/(?P<type>.+)?$', - 'show_person', name='show-person'), - url(r'department-by-state/(?P<state_id>.+)?$', 'department_by_state', - name='department-by-state'), - url(r'autocomplete-town/?$', 'autocomplete_town', - name='autocomplete-town'), - url(r'autocomplete-advanced-town/(?P<department_id>[0-9]+[ABab]?)?$', +urlpatterns += patterns( + 'ishtar_common.views', + # General + url(r'dashboard-main/$', 'dashboard_main', + name='dashboard-main'), + url(r'dashboard-main/(?P<item_name>[a-z-]+)/$', 'dashboard_main_detail', + name='dashboard-main-detail'), + url(r'update-current-item/$', 'update_current_item', + name='update-current-item'), + url(r'new-person/(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$', + 'new_person', name='new-person'), + url(r'new-person-noorga/' + r'(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$', + 'new_person_noorga', name='new-person-noorga'), + url(r'autocomplete-person(?:/([0-9_]+))?(?:/([0-9_]*))?/(user)?$', + 'autocomplete_person', name='autocomplete-person'), + url(r'get-person/(?P<type>.+)?$', 'get_person', + name='get-person'), + url(r'show-person(?:/(?P<pk>.+))?/(?P<type>.+)?$', + 'show_person', name='show-person'), + url(r'department-by-state/(?P<state_id>.+)?$', 'department_by_state', + name='department-by-state'), + url(r'autocomplete-town/?$', 'autocomplete_town', + name='autocomplete-town'), + url(r'autocomplete-advanced-town/(?P<department_id>[0-9]+[ABab]?)?$', 'autocomplete_advanced_town', name='autocomplete-advanced-town'), - url(r'autocomplete-department/?$', 'autocomplete_department', - name='autocomplete-department'), - url(r'new-author/(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$', - 'new_author', name='new-author'), - url(r'autocomplete-author/$', 'autocomplete_author', - name='autocomplete-author'), - url(r'new-organization/(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$', - 'new_organization', name='new-organization'), - url(r'get-organization/(?P<type>.+)?$', 'get_organization', - name='get-organization'), - url(r'show-organization(?:/(?P<pk>.+))?/(?P<type>.+)?$', - 'show_organization', name='show-organization'), - url(r'autocomplete-organization/([0-9_]+)?$', - 'autocomplete_organization', name='autocomplete-organization'), - url(r'admin-globalvar/', views.GlobalVarEdit.as_view(), + url(r'autocomplete-department/?$', 'autocomplete_department', + name='autocomplete-department'), + url(r'new-author/(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$', + 'new_author', name='new-author'), + url(r'autocomplete-author/$', 'autocomplete_author', + name='autocomplete-author'), + url(r'new-organization/(?:(?P<parent_name>[^/]+)/)?' + r'(?:(?P<limits>[^/]+)/)?$', + 'new_organization', name='new-organization'), + url(r'get-organization/(?P<type>.+)?$', 'get_organization', + name='get-organization'), + url(r'show-organization(?:/(?P<pk>.+))?/(?P<type>.+)?$', + 'show_organization', name='show-organization'), + url(r'autocomplete-organization/([0-9_]+)?$', + 'autocomplete_organization', name='autocomplete-organization'), + url(r'admin-globalvar/', views.GlobalVarEdit.as_view(), name='admin-globalvar'), - url(r'person_merge/(?:(?P<page>\d+)/)?$', 'person_merge', name='person_merge'), - url(r'organization_merge/(?:(?P<page>\d+)/)?$', 'organization_merge', - name='organization_merge'), - url(r'reset/$', 'reset_wizards', name='reset_wizards'), - url(r'(?P<action_slug>' + actions + r')/$', 'action', name='action'), + url(r'person_merge/(?:(?P<page>\d+)/)?$', 'person_merge', + name='person_merge'), + url(r'organization_merge/(?:(?P<page>\d+)/)?$', 'organization_merge', + name='organization_merge'), + url(r'reset/$', 'reset_wizards', name='reset_wizards'), + url(r'(?P<action_slug>' + actions + r')/$', 'action', name='action'), ) if settings.DEBUG: - urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + urlpatterns += static(settings.STATIC_URL, + document_root=settings.STATIC_ROOT) diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 0058821b2..fc8151d8d 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -19,14 +19,14 @@ from tidylib import tidy_document as tidy -import re import csv -import json +import cStringIO as StringIO import datetime +import ho.pisa as pisa +import json import optparse -import cStringIO as StringIO +import re from tempfile import NamedTemporaryFile -import ho.pisa as pisa import unicodedata from extra_views import ModelFormSetView @@ -34,17 +34,17 @@ from extra_views import ModelFormSetView from django.conf import settings from django.contrib.auth import logout from django.contrib.auth.decorators import login_required -from django.core import serializers from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse, NoReverseMatch -from django.db.models import Q, F, ImageField -from django.forms.models import model_to_dict, modelformset_factory -from django.http import HttpResponse, Http404, HttpResponseRedirect +from django.db.models import Q, ImageField +from django.forms.models import modelformset_factory +from django.http import HttpResponse, Http404, HttpResponseRedirect, \ + HttpResponseBadRequest from django.shortcuts import render_to_response, redirect from django.template import RequestContext, loader from django.utils.decorators import method_decorator from django.utils.translation import ugettext, ugettext_lazy as _ -from django.views.generic import ListView, UpdateView, CreateView +from django.views.generic import ListView, UpdateView from django.views.generic.edit import CreateView, DeleteView from xhtml2odt import xhtml2odt @@ -56,11 +56,14 @@ from archaeological_operations.forms import DashboardForm as DashboardFormOpe from ishtar_common.forms import FinalForm, FinalDeleteForm from ishtar_common import forms_common as forms from ishtar_common import wizards +from ishtar_common.models import HistoryError + import models -CSV_OPTIONS = {'delimiter':';', 'quotechar':'"', 'quoting':csv.QUOTE_ALL} +CSV_OPTIONS = {'delimiter': ';', 'quotechar': '"', 'quoting': csv.QUOTE_ALL} ENCODING = settings.ENCODING or 'utf-8' + def index(request): """ Main page @@ -68,69 +71,70 @@ def index(request): dct = {} try: return render_to_response('index.html', dct, - context_instance=RequestContext(request)) + context_instance=RequestContext(request)) except NoReverseMatch: # probably rights exception (rights revoked) logout(request) return render_to_response('index.html', dct, - context_instance=RequestContext(request)) + context_instance=RequestContext(request)) + +person_search_wizard = wizards.SearchWizard.as_view( + [('general-person_search', forms.PersonFormSelection)], + label=_(u"Person search"), + url_name='person_search',) + +person_creation_wizard = wizards.PersonWizard.as_view( + [('identity-person_creation', forms.SimplePersonForm), + ('person_type-person_creation', forms.PersonTypeForm), + ('final-person_creation', FinalForm)], + label=_(u"New person"), + url_name='person_creation') + +person_modification_wizard = wizards.PersonModifWizard.as_view( + [('selec-person_modification', forms.PersonFormSelection), + ('identity-person_modification', forms.SimplePersonForm), + ('person_type-person_creation', forms.PersonTypeForm), + ('final-person_modification', FinalForm)], + label=_(u"Person modification"), + url_name='person_modification') + +person_deletion_wizard = wizards.PersonDeletionWizard.as_view( + [('selec-person_deletion', forms.PersonFormSelection), + ('final-person_deletion', FinalDeleteForm)], + label=_(u"Person deletion"), + url_name='person_deletion',) + +organization_search_wizard = wizards.SearchWizard.as_view( + [('general-organization_search', forms.OrganizationFormSelection)], + label=_(u"Organization search"), + url_name='organization_search',) + +organization_creation_wizard = wizards.OrganizationWizard.as_view( + [('identity-organization_creation', forms.OrganizationForm), + ('final-organization_creation', FinalForm)], + label=_(u"New organization"), + url_name='organization_creation') + +organization_modification_wizard = wizards.OrganizationModifWizard.as_view( + [('selec-organization_modification', forms.OrganizationFormSelection), + ('identity-organization_modification', forms.OrganizationForm), + ('final-organization_modification', FinalForm)], + label=_(u"Organization modification"), + url_name='organization_modification') + +organization_deletion_wizard = wizards.OrganizationDeletionWizard.as_view( + [('selec-organization_deletion', forms.OrganizationFormSelection), + ('final-organization_deletion', FinalDeleteForm)], + label=_(u"Organization deletion"), + url_name='organization_deletion',) + +account_management_wizard = wizards.AccountWizard.as_view( + [('selec-account_management', forms.PersonFormSelection), + ('account-account_management', forms.AccountForm), + ('final-account_management', forms.FinalAccountForm)], + label=_(u"Account management"), + url_name='account_management',) -person_search_wizard = wizards.SearchWizard.as_view([ - ('general-person_search', forms.PersonFormSelection)], - label=_(u"Person search"), - url_name='person_search',) - -person_creation_wizard = wizards.PersonWizard.as_view([ - ('identity-person_creation', forms.SimplePersonForm), - ('person_type-person_creation', forms.PersonTypeForm), - ('final-person_creation', FinalForm)], - label=_(u"New person"), - url_name='person_creation') - -person_modification_wizard = wizards.PersonModifWizard.as_view([ - ('selec-person_modification', forms.PersonFormSelection), - ('identity-person_modification', forms.SimplePersonForm), - ('person_type-person_creation', forms.PersonTypeForm), - ('final-person_modification', FinalForm)], - label=_(u"Person modification"), - url_name='person_modification') - -person_deletion_wizard = wizards.PersonDeletionWizard.as_view([ - ('selec-person_deletion', forms.PersonFormSelection), - ('final-person_deletion', FinalDeleteForm)], - label=_(u"Person deletion"), - url_name='person_deletion',) - -organization_search_wizard = wizards.SearchWizard.as_view([ - ('general-organization_search', forms.OrganizationFormSelection)], - label=_(u"Organization search"), - url_name='organization_search',) - -organization_creation_wizard = wizards.OrganizationWizard.as_view([ - ('identity-organization_creation', forms.OrganizationForm), - ('final-organization_creation', FinalForm)], - label=_(u"New organization"), - url_name='organization_creation') - -organization_modification_wizard = wizards.OrganizationModifWizard.as_view([ - ('selec-organization_modification', forms.OrganizationFormSelection), - ('identity-organization_modification', forms.OrganizationForm), - ('final-organization_modification', FinalForm)], - label=_(u"Organization modification"), - url_name='organization_modification') - -organization_deletion_wizard = wizards.OrganizationDeletionWizard.as_view([ - ('selec-organization_deletion', forms.OrganizationFormSelection), - ('final-organization_deletion', FinalDeleteForm)], - label=_(u"Organization deletion"), - url_name='organization_deletion',) - -account_management_wizard = wizards.AccountWizard.as_view([ - ('selec-account_management', forms.PersonFormSelection), - ('account-account_management', forms.AccountForm), - ('final-account_management', forms.FinalAccountForm)], - label=_(u"Account management"), - url_name='account_management',) def update_current_item(request): if not request.is_ajax() and not request.method == 'POST': @@ -139,15 +143,18 @@ def update_current_item(request): request.session[request.POST['item']] = request.POST['value'] return HttpResponse('ok') + def check_permission(request, action_slug, obj_id=None): if action_slug not in menu.items: - #! TODO + # TODO return True if obj_id: return menu.items[action_slug].is_available(request.user, obj_id) return menu.items[action_slug].can_be_available(request.user) -def autocomplete_person(request, person_types=None, is_ishtar_user=None): + +def autocomplete_person(request, person_types=None, attached_to=None, + is_ishtar_user=None): if not request.user.has_perm('ishtar_common.view_person', models.Person) and \ not request.user.has_perm('ishtar_common.view_own_person', models.Person) \ and not request.user.ishtaruser.has_right('person_search'): @@ -162,9 +169,13 @@ def autocomplete_person(request, person_types=None, is_ishtar_user=None): return HttpResponseBadRequest() query = Q() for q in q.split(' '): - query = query & (Q(name__icontains=q) | Q(surname__icontains=q) | \ - Q(email__icontains=q) | Q(attached_to__name__icontains=q)) - if person_types and unicode(person_types) != '0': + query = query & (Q(name__icontains=q) | Q(surname__icontains=q) | + Q(email__icontains=q) | + Q(attached_to__name__icontains=q)) + if attached_to: + query = query & Q(attached_to__pk__in=attached_to.split('_')) + + if person_types and unicode(person_types) != '0': try: typs = [int(tp) for tp in person_types.split('_') if tp] typ = models.PersonType.objects.filter(pk__in=typs).all() @@ -175,48 +186,51 @@ def autocomplete_person(request, person_types=None, is_ishtar_user=None): query = query & Q(ishtaruser__isnull=False) limit = 20 persons = models.Person.objects.filter(query)[:limit] - data = json.dumps([{'id':person.pk, 'value':unicode(person)} - for person in persons if person]) + data = json.dumps([{'id': person.pk, 'value': unicode(person)} + for person in persons if person]) return HttpResponse(data, mimetype='text/plain') + def autocomplete_department(request): if not request.GET.get('term'): return HttpResponse(mimetype='text/plain') q = request.GET.get('term') - q = unicodedata.normalize("NFKD", q).encode('ascii','ignore') + q = unicodedata.normalize("NFKD", q).encode('ascii', 'ignore') query = Q() for q in q.split(' '): extra = (Q(label__icontains=q) | Q(number__istartswith=q)) query = query & extra limit = 20 departments = models.Department.objects.filter(query)[:limit] - data = json.dumps([{'id':department.pk, 'value':unicode(department)} - for department in departments]) + data = json.dumps([{'id': department.pk, 'value': unicode(department)} + for department in departments]) return HttpResponse(data, mimetype='text/plain') + def autocomplete_town(request): if not request.GET.get('term'): return HttpResponse(mimetype='text/plain') q = request.GET.get('term') - q = unicodedata.normalize("NFKD", q).encode('ascii','ignore') + q = unicodedata.normalize("NFKD", q).encode('ascii', 'ignore') query = Q() for q in q.split(' '): extra = Q(name__icontains=q) if settings.COUNTRY == 'fr': - extra = (extra | Q(numero_insee__istartswith=q) | \ - Q(departement__label__istartswith=q)) + extra = (extra | Q(numero_insee__istartswith=q) | + Q(departement__label__istartswith=q)) query = query & extra limit = 20 towns = models.Town.objects.filter(query)[:limit] - data = json.dumps([{'id':town.pk, 'value':unicode(town)} - for town in towns]) + data = json.dumps([{'id': town.pk, 'value': unicode(town)} + for town in towns]) return HttpResponse(data, mimetype='text/plain') + def autocomplete_advanced_town(request, department_id=None, state_id=None): if not request.GET.get('term'): return HttpResponse(mimetype='text/plain') q = request.GET.get('term') - q = unicodedata.normalize("NFKD", q).encode('ascii','ignore') + q = unicodedata.normalize("NFKD", q).encode('ascii', 'ignore') query = Q() for q in q.split(' '): extra = Q(name__icontains=q) @@ -236,25 +250,24 @@ def autocomplete_advanced_town(request, department_id=None, state_id=None): val = town.name if hasattr(town, 'numero_insee'): val += " (%s)" % town.numero_insee - result.append({'id':town.pk, 'value':val}) + result.append({'id': town.pk, 'value': val}) data = json.dumps(result) return HttpResponse(data, mimetype='text/plain') + def department_by_state(request, state_id=''): if not state_id: data = [] else: departments = models.Department.objects.filter(state__number=state_id) - data = json.dumps([{'id':department.pk, 'number':department.number, - 'value':unicode(department)} - for department in departments]) + data = json.dumps([{'id': department.pk, 'number': department.number, + 'value': unicode(department)} + for department in departments]) return HttpResponse(data, mimetype='text/plain') -from types import NoneType - def format_val(val): - if type(val) == NoneType: + if val is None: return u"" if type(val) == bool: if val: @@ -267,33 +280,33 @@ HIERARCHIC_LEVELS = 5 HIERARCHIC_FIELDS = ['periods', 'period', 'unit', 'material_type', 'conservatory_state'] PRIVATE_FIELDS = ('id', 'history_modifier', 'order') + + def get_item(model, func_name, default_name, extra_request_keys=[], - base_request={}, bool_fields=[], reversed_bool_fields=[], - dated_fields=[], associated_models=[], relative_session_names={}, - specific_perms=[]): + base_request={}, bool_fields=[], reversed_bool_fields=[], + dated_fields=[], associated_models=[], relative_session_names={}, + specific_perms=[]): """ Generic treatment of tables """ def func(request, data_type='json', full=False, **dct): # check rights - own = True # more restrictive by default + own = True # more restrictive by default allowed = False for perm, lbl in model._meta.permissions: # if not specific any perm is relevant (read right) if specific_perms and perm not in specific_perms: continue if request.user.has_perm(model._meta.app_label + '.' + perm) \ - or (request.user.is_authenticated() - and request.user.ishtaruser.has_right(perm)): + or (request.user.is_authenticated() + and request.user.ishtaruser.has_right(perm)): allowed = True if "_own_" not in perm: own = False - break # max right reach - EMPTY, mimetype = '', 'text/plain' + break # max right reach + EMPTY = '' if 'type' in dct: data_type = dct.pop('type') - if data_type == 'csv': - mimetype = 'text/csv' if not data_type: EMPTY = '[]' data_type = 'json' @@ -301,18 +314,23 @@ def get_item(model, func_name, default_name, extra_request_keys=[], return HttpResponse(EMPTY, mimetype='text/plain') fields = [model._meta.get_field_by_name(k)[0] for k in model._meta.get_all_field_names()] - request_keys = dict([(field.name, - field.name + (hasattr(field, 'rel') and field.rel and '__pk' or '')) - for field in fields]) + request_keys = dict([ + (field.name, + field.name + (hasattr(field, 'rel') and field.rel and '__pk' + or '')) + for field in fields]) for associated_model, key in associated_models: - associated_fields = [associated_model._meta.get_field_by_name(k)[0] - for k in associated_model._meta.get_all_field_names()] - request_keys.update(dict([(key + "__" + field.name, - key + "__" + field.name + (hasattr(field, 'rel') and - field.rel and '__pk' or '')) - for field in associated_fields])) + associated_fields = [ + associated_model._meta.get_field_by_name(k)[0] + for k in associated_model._meta.get_all_field_names()] + request_keys.update( + dict([(key + "__" + field.name, + key + "__" + field.name + + (hasattr(field, 'rel') and field.rel and '__pk' or '')) + for field in associated_fields])) request_keys.update(extra_request_keys) - request_items = request.method == 'POST' and request.POST or request.GET + request_items = request.method == 'POST' and request.POST \ + or request.GET dct = base_request.copy() and_reqs, or_reqs = [], [] try: @@ -327,14 +345,15 @@ def get_item(model, func_name, default_name, extra_request_keys=[], if not dct and 'submited' not in request_items: if default_name in request.session and \ request.session[default_name]: - dct = {"pk":request.session[default_name]} + dct = {"pk": request.session[default_name]} else: for name in relative_session_names.keys(): if name in request.session and request.session[name]: k = relative_session_names[name] - dct = {k:request.session[name]} + dct = {k: request.session[name]} break - if (not dct or data_type == 'csv') and func_name in request.session: + if (not dct or data_type == 'csv') \ + and func_name in request.session: dct = request.session[func_name] else: request.session[func_name] = dct @@ -351,7 +370,8 @@ def get_item(model, func_name, default_name, extra_request_keys=[], if k.endswith('__isnull') and \ isinstance(c_field, ImageField): if dct[k]: - or_reqs.append((k, {k.split('__')[0]+'__exact':''})) + or_reqs.append( + (k, {k.split('__')[0]+'__exact': ''})) else: dct[k.split('__')[0]+'__regex'] = '.{1}.*' for k in dated_fields: @@ -362,8 +382,8 @@ def get_item(model, func_name, default_name, extra_request_keys=[], items = dct[k].split('/') assert len(items) == 3 dct[k] = datetime.date(*map(lambda x: int(x), - reversed(items)) - ).strftime('%Y-%m-%d') + reversed(items)))\ + .strftime('%Y-%m-%d') except AssertionError: dct.pop(k) # manage hierarchic conditions @@ -374,18 +394,18 @@ def get_item(model, func_name, default_name, extra_request_keys=[], q = None for idx, r in enumerate(req): if not idx: - q = Q(**{r:val}) + q = Q(**{r: val}) else: - q = q | Q(**{r:val}) + q = q | Q(**{r: val}) and_reqs.append(q) break elif req.endswith(k_hr + '__pk'): val = dct.pop(req) - reqs = Q(**{req:val}) + reqs = Q(**{req: val}) req = req[:-2] + '__' for idx in xrange(HIERARCHIC_LEVELS): req = req[:-2] + 'parent__pk' - q = Q(**{req:val}) + q = Q(**{req: val}) reqs = reqs | q and_reqs.append(reqs) break @@ -405,7 +425,7 @@ def get_item(model, func_name, default_name, extra_request_keys=[], # table cols table_cols = full and [field.name for field in model._meta.fields if field.name not in PRIVATE_FIELDS] \ - or model.TABLE_COLS + or model.TABLE_COLS # manage sort tables manual_sort_key = None order = request_items.get('sord') @@ -461,14 +481,15 @@ def get_item(model, func_name, default_name, extra_request_keys=[], for ky in k.split('.'): new_vals = [] for val in vals: - if hasattr(val, 'all'): # manage related objects + if hasattr(val, 'all'): # manage related objects val = list(val.all()) for v in val: new_vals.append(getattr(v, ky)) elif val: new_vals.append(getattr(val, ky)) vals = new_vals - if vals and hasattr(vals[0], 'all'): # manage last related objects + # manage last related objects + if vals and hasattr(vals[0], 'all'): new_vals = [] for val in vals: new_vals += list(val.all()) @@ -478,32 +499,33 @@ def get_item(model, func_name, default_name, extra_request_keys=[], if manual_sort_key: # +1 because the id is added as a first col idx_col = table_cols.index(manual_sort_key) + 1 - datas = sorted(datas, key=lambda x:x[idx_col]) + datas = sorted(datas, key=lambda x: x[idx_col]) if sign == '-': datas = reversed(datas) datas = list(datas)[start:end] - link_template = "<a class='display_details' href='#' onclick='load_window(\"%%s\")'>%s</a>" % \ + link_template = "<a class='display_details' href='#' "\ + "onclick='load_window(\"%%s\")'>%s</a>" % \ (unicode(_("Details"))) if data_type == "json": rows = [] for data in datas: try: lnk = link_template % reverse('show-'+default_name, - args=[data[0], '']) + args=[data[0], '']) except NoReverseMatch: print '"show-' + default_name + "\" args (" + \ unicode(data[0]) + ") url not available" lnk = '' - res = {'id':data[0], 'link':lnk} + res = {'id': data[0], 'link': lnk} for idx, value in enumerate(data[1:]): if value: res[table_cols[idx].split('.')[-1]] = value rows.append(res) data = json.dumps({ - "records":items_nb, - "rows":rows, - "page":page_nb, - "total":(items_nb/row_nb + 1) if row_nb else items_nb, + "records": items_nb, + "rows": rows, + "page": page_nb, + "total": (items_nb/row_nb + 1) if row_nb else items_nb, }) return HttpResponse(data, mimetype='text/plain') elif data_type == "csv": @@ -511,7 +533,8 @@ def get_item(model, func_name, default_name, extra_request_keys=[], n = datetime.datetime.now() filename = u'%s_%s.csv' % (default_name, n.strftime('%Y%m%d-%H%M%S')) - response['Content-Disposition'] = 'attachment; filename=%s'%filename + response['Content-Disposition'] = 'attachment; filename=%s'\ + % filename writer = csv.writer(response, **CSV_OPTIONS) col_names = [] for field_name in table_cols: @@ -529,6 +552,7 @@ def get_item(model, func_name, default_name, extra_request_keys=[], return func + def show_item(model, name, extra_dct=None): def func(request, pk, **dct): try: @@ -540,15 +564,15 @@ def show_item(model, name, extra_dct=None): ).split('/')[:-2]) + u"/" dct['current_window_url'] = url_name date = 'date' in dct and dct.pop('date') - dct['window_id'] = "%s-%d-%s" % (name, item.pk, - datetime.datetime.now().strftime('%M%s')) + dct['window_id'] = "%s-%d-%s" % ( + name, item.pk, datetime.datetime.now().strftime('%M%s')) if hasattr(item, 'history'): if date: try: date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f') item = item.get_previous(date=date) - assert item != None + assert item is not None except (ValueError, AssertionError): return HttpResponse(None, mimetype='text/plain') dct['previous'] = item._previous @@ -565,7 +589,6 @@ def show_item(model, name, extra_dct=None): dct.update(extra_dct(request, item)) context_instance = RequestContext(request) context_instance.update(dct) - n = datetime.datetime.now() filename = "" if hasattr(item, 'history_object'): filename = item.history_object.associated_filename @@ -575,9 +598,9 @@ def show_item(model, name, extra_dct=None): tpl = loader.get_template('ishtar/sheet_%s.html' % name) content = tpl.render(context_instance) try: - tidy_options = {'output-xhtml':1, 'indent':1, - 'tidy-mark':0, 'doctype':'auto', - 'add-xml-decl':1, 'wrap':1} + tidy_options = {'output-xhtml': 1, 'indent': 1, + 'tidy-mark': 0, 'doctype': 'auto', + 'add-xml-decl': 1, 'wrap': 1} html, errors = tidy(content, options=tidy_options) html = html.encode('utf-8').replace(" ", " ") html = re.sub('<pre([^>]*)>\n', '<pre\\1>', html) @@ -602,12 +625,12 @@ def show_item(model, name, extra_dct=None): odtfile.open() odtfile.import_xhtml(html) odtfile = odtfile.save() - except xhtml2odt.ODTExportError, ex: + except xhtml2odt.ODTExportError: return HttpResponse(content, content_type="application/xhtml") response = HttpResponse( - mimetype='application/vnd.oasis.opendocument.text') + mimetype='application/vnd.oasis.opendocument.text') response['Content-Disposition'] = 'attachment; filename=%s.odt' % \ - filename + filename response.write(odtfile) return response elif doc_type == 'pdf': @@ -621,7 +644,7 @@ def show_item(model, name, extra_dct=None): response = HttpResponse(result.getvalue(), mimetype='application/pdf') response['Content-Disposition'] = 'attachment; filename=%s.pdf' % \ - filename + filename if not pdf.err: return response return HttpResponse(content, content_type="application/xhtml") @@ -631,6 +654,7 @@ def show_item(model, name, extra_dct=None): return HttpResponse(content, content_type="application/xhtml") return func + def revert_item(model): def func(request, pk, date, **dct): try: @@ -642,11 +666,12 @@ def revert_item(model): return HttpResponse("True", mimetype='text/plain') return func + def autocomplete_organization(request, orga_type=None): if (not request.user.has_perm('ishtar_common.view_organization', - models.Organization) and \ - not request.user.has_perm('ishtar_common.view_own_organization', - models.Organization) + models.Organization) and + not request.user.has_perm('ishtar_common.view_own_organization', + models.Organization) and not request.user.ishtaruser.has_right('person_search')): return HttpResponse(mimetype='text/plain') if not request.GET.get('term'): @@ -665,14 +690,15 @@ def autocomplete_organization(request, orga_type=None): pass limit = 15 organizations = models.Organization.objects.filter(query)[:limit] - data = json.dumps([{'id':org.pk, 'value':unicode(org)} - for org in organizations]) + data = json.dumps([{'id': org.pk, 'value': unicode(org)} + for org in organizations]) return HttpResponse(data, mimetype='text/plain') + def autocomplete_author(request): if not request.user.has_perm('ishtar_common.view_author', models.Author)\ and not request.user.has_perm('ishtar_common.view_own_author', - models.Author) : + models.Author): return HttpResponse(mimetype='text/plain') if not request.GET.get('term'): return HttpResponse(mimetype='text/plain') @@ -680,23 +706,24 @@ def autocomplete_author(request): query = Q() for q in q.split(' '): extra = Q(person__name__icontains=q) | \ - Q(person__surname__icontains=q) | \ - Q(person__email__icontains=q) | \ - Q(author_type__label__icontains=q) + Q(person__surname__icontains=q) | \ + Q(person__email__icontains=q) | \ + Q(author_type__label__icontains=q) query = query & extra limit = 15 authors = models.Author.objects.filter(query)[:limit] - data = json.dumps([{'id':author.pk, 'value':unicode(author)} - for author in authors]) + data = json.dumps([{'id': author.pk, 'value': unicode(author)} + for author in authors]) return HttpResponse(data, mimetype='text/plain') + def new_item(model, frm): def func(request, parent_name, limits=''): model_name = model._meta.object_name if not check_permission(request, 'add_'+model_name.lower()): not_permitted_msg = ugettext(u"Operation not permitted.") return HttpResponse(not_permitted_msg) - dct = {'title':unicode(_(u'New %s' % model_name.lower()))} + dct = {'title': unicode(_(u'New %s' % model_name.lower()))} if request.method == 'POST': dct['form'] = frm(request.POST, limits=limits) if dct['form'].is_valid(): @@ -708,8 +735,9 @@ def new_item(model, frm): if dct['parent_pk'] and '_select_' in dct['parent_pk']: parents = dct['parent_pk'].split('_') dct['parent_pk'] = "_".join([parents[0]] + parents[2:]) - return render_to_response('window.html', dct, - context_instance=RequestContext(request)) + return render_to_response( + 'window.html', dct, + context_instance=RequestContext(request)) else: dct['form'] = frm(limits=limits) return render_to_response('window.html', dct, @@ -717,24 +745,28 @@ def new_item(model, frm): return func new_person = new_item(models.Person, forms.PersonForm) +new_person_noorga = new_item(models.Person, forms.NoOrgaPersonForm) new_organization = new_item(models.Organization, forms.OrganizationForm) show_organization = show_item(models.Organization, 'organization') -get_organization = get_item(models.Organization, - 'get_organization', 'organization', - extra_request_keys={ - 'name':'name__icontains', - 'organization_type':'organization_type__pk__in', - },) +get_organization = get_item( + models.Organization, + 'get_organization', 'organization', + extra_request_keys={ + 'name': 'name__icontains', + 'organization_type': 'organization_type__pk__in', + }) new_author = new_item(models.Author, forms.AuthorForm) show_person = show_item(models.Person, 'person') -get_person = get_item(models.Person, - 'get_person', 'person', - extra_request_keys={ - 'name':'name__icontains', - 'surname':'surname__icontains', - 'attached_to':'attached_to__pk', - 'person_types':'person_types__pk__in', - },) +get_person = get_item( + models.Person, + 'get_person', 'person', + extra_request_keys={ + 'name': 'name__icontains', + 'surname': 'surname__icontains', + 'attached_to': 'attached_to__pk', + 'person_types': 'person_types__pk__in', + }) + def action(request, action_slug, obj_id=None, *args, **kwargs): """ @@ -744,7 +776,6 @@ def action(request, action_slug, obj_id=None, *args, **kwargs): not_permitted_msg = ugettext(u"Operation not permitted.") return HttpResponse(not_permitted_msg) request.session['CURRENT_ACTION'] = action_slug - associated_wizard = action_slug + '_wizard' dct = {} globals_dct = globals() if action_slug in globals_dct: @@ -752,6 +783,7 @@ def action(request, action_slug, obj_id=None, *args, **kwargs): return render_to_response('index.html', dct, context_instance=RequestContext(request)) + def dashboard_main(request, dct, obj_id=None, *args, **kwargs): """ Main dashboard @@ -764,9 +796,9 @@ def dashboard_main(request, dct, obj_id=None, *args, **kwargs): app_list.append((_(u"Context records"), 'contextrecords')) if 'archaeological_finds' in settings.INSTALLED_APPS: app_list.append((_(u"Finds"), 'finds')) - dct = {'app_list':app_list} + dct = {'app_list': app_list} return render_to_response('ishtar/dashboards/dashboard_main.html', dct, - context_instance=RequestContext(request)) + context_instance=RequestContext(request)) DASHBOARD_FORMS = {} if 'archaeological_files' in settings.INSTALLED_APPS: @@ -775,20 +807,21 @@ if 'archaeological_files' in settings.INSTALLED_APPS: DASHBOARD_FORMS['operations'] = DashboardFormOpe + def dashboard_main_detail(request, item_name): """ Specific tab of the main dashboard """ if item_name == 'users': - dct = {'ishtar_users':models.UserDashboard()} + dct = {'ishtar_users': models.UserDashboard()} return render_to_response( - 'ishtar/dashboards/dashboard_main_detail_users.html', - dct, context_instance=RequestContext(request)) + 'ishtar/dashboards/dashboard_main_detail_users.html', + dct, context_instance=RequestContext(request)) form = None - slicing, date_source, fltr, show_detail = 'year', None, {}, False - if (item_name == 'files' and \ - 'archaeological_files' in settings.INSTALLED_APPS) \ - or item_name == 'operations': + slicing, date_source, fltr, show_detail = 'year', None, {}, False + if (item_name == 'files' and + 'archaeological_files' in settings.INSTALLED_APPS) \ + or item_name == 'operations': slicing = 'month' if item_name in DASHBOARD_FORMS: if request.method == 'POST': @@ -803,16 +836,16 @@ def dashboard_main_detail(request, item_name): else: form = DASHBOARD_FORMS[item_name]() lbl, dashboard = None, None - if (item_name == 'files' and \ - 'archaeological_files' in settings.INSTALLED_APPS) \ - or item_name == 'operations': - dashboard_kwargs = {'slice':slicing, 'fltr':fltr, - 'show_detail':show_detail} + if (item_name == 'files' and + 'archaeological_files' in settings.INSTALLED_APPS) \ + or item_name == 'operations': + dashboard_kwargs = {'slice': slicing, 'fltr': fltr, + 'show_detail': show_detail} # date_source is only relevant when the form has set one if date_source: dashboard_kwargs['date_source'] = date_source if item_name == 'files' and \ - 'archaeological_files' in settings.INSTALLED_APPS: + 'archaeological_files' in settings.INSTALLED_APPS: from archaeological_files.models import File lbl, dashboard = (_(u"Archaeological files"), models.Dashboard(File, **dashboard_kwargs)) @@ -821,26 +854,29 @@ def dashboard_main_detail(request, item_name): lbl, dashboard = (_(u"Operations"), models.Dashboard(Operation, **dashboard_kwargs)) if item_name == 'contextrecords' and \ - 'archaeological_context_records' in settings.INSTALLED_APPS: + 'archaeological_context_records' in settings.INSTALLED_APPS: from archaeological_context_records.models import ContextRecord - lbl, dashboard = (_(u"Context records"), models.Dashboard(ContextRecord, - slice=slicing, fltr=fltr)) + lbl, dashboard = ( + _(u"Context records"), + models.Dashboard(ContextRecord, slice=slicing, fltr=fltr)) if item_name == 'finds' and \ - 'archaeological_finds' in settings.INSTALLED_APPS: + 'archaeological_finds' in settings.INSTALLED_APPS: from archaeological_finds.models import Find lbl, dashboard = (_(u"Finds"), models.Dashboard(Find, - slice=slicing, fltr=fltr)) + slice=slicing, + fltr=fltr)) if not lbl: raise Http404 - dct = {'lbl':lbl, 'dashboard':dashboard, - 'item_name':item_name.replace('-', '_'), + dct = {'lbl': lbl, 'dashboard': dashboard, + 'item_name': item_name.replace('-', '_'), 'VALUE_QUOTE': '' if slicing == "year" else "'", - 'form':form, 'slicing':slicing} + 'form': form, 'slicing': slicing} n = datetime.datetime.now() dct['unique_id'] = dct['item_name'] + "_" + \ - '%d_%d_%d' % (n.minute, n.second, n.microsecond) + '%d_%d_%d' % (n.minute, n.second, n.microsecond) return render_to_response('ishtar/dashboards/dashboard_main_detail.html', - dct, context_instance=RequestContext(request)) + dct, context_instance=RequestContext(request)) + def reset_wizards(request): # dynamicaly execute each reset_wizards of each ishtar app @@ -857,18 +893,21 @@ def reset_wizards(request): return redirect(reverse('start')) ITEM_PER_PAGE = 20 + + def merge_action(model, form, key): def merge(request, page=1): current_url = key + '_merge' if not page: page = 1 page = int(page) - FormSet = modelformset_factory(model.merge_candidate.through, - form=form, formset=forms.MergeFormSet ,extra=0) + FormSet = modelformset_factory( + model.merge_candidate.through, form=form, + formset=forms.MergeFormSet, extra=0) q = model.merge_candidate.through.objects - context = {'current_url':current_url, - 'current_page':page, - 'max_page':q.count()/ITEM_PER_PAGE} + context = {'current_url': current_url, + 'current_page': page, + 'max_page': q.count()/ITEM_PER_PAGE} if page < context["max_page"]: context['next_page'] = page + 1 if page > 1: @@ -885,25 +924,32 @@ def merge_action(model, form, key): context['formset'] = FormSet(request.POST, queryset=queryset) if context['formset'].is_valid(): context['formset'].merge() - return redirect(reverse(current_url, kwargs={'page':page})) + return redirect(reverse(current_url, kwargs={'page': page})) else: context['formset'] = FormSet(queryset=queryset) - return render_to_response('ishtar/merge_'+key+'.html', context, - context_instance=RequestContext(request)) + return render_to_response( + 'ishtar/merge_'+key+'.html', context, + context_instance=RequestContext(request)) return merge person_merge = merge_action(models.Person, forms.MergePersonForm, 'person') -organization_merge = merge_action(models.Organization, forms.MergeOrganizationForm, - 'organization') +organization_merge = merge_action( + models.Organization, + forms.MergeOrganizationForm, + 'organization' +) + class IshtarMixin(object): page_name = u"" + def get_context_data(self, **kwargs): context = super(IshtarMixin, self).get_context_data(**kwargs) context['page_name'] = self.page_name return context + class LoginRequiredMixin(object): @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): @@ -915,12 +961,14 @@ class LoginRequiredMixin(object): return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs) + class AdminLoginRequiredMixin(LoginRequiredMixin): def dispatch(self, request, *args, **kwargs): if not request.user.is_staff: return redirect(reverse('start')) - return super(AdminLoginRequiredMixin, self).dispatch(request, *args, - **kwargs) + return super(AdminLoginRequiredMixin, self).dispatch( + request, *args, **kwargs) + class GlobalVarEdit(IshtarMixin, AdminLoginRequiredMixin, ModelFormSetView): template_name = 'ishtar/formset.html' @@ -930,6 +978,7 @@ class GlobalVarEdit(IshtarMixin, AdminLoginRequiredMixin, ModelFormSetView): page_name = _(u"Global variables") fields = ['slug', 'value', 'description'] + class NewImportView(IshtarMixin, LoginRequiredMixin, CreateView): template_name = 'ishtar/form.html' model = models.Import @@ -944,6 +993,7 @@ class NewImportView(IshtarMixin, LoginRequiredMixin, CreateView): self.object = form.save(user=user) return HttpResponseRedirect(self.get_success_url()) + class ImportListView(IshtarMixin, LoginRequiredMixin, ListView): template_name = 'ishtar/import_list.html' model = models.Import @@ -952,8 +1002,8 @@ class ImportListView(IshtarMixin, LoginRequiredMixin, ListView): def get_queryset(self): user = models.IshtarUser.objects.get(pk=self.request.user.pk) - return self.model.objects.filter(user=user).exclude(state='AC' - ).order_by('-creation_date') + return self.model.objects.filter(user=user).exclude( + state='AC').order_by('-creation_date') def post(self, request, *args, **kwargs): for field in request.POST: @@ -972,7 +1022,7 @@ class ImportListView(IshtarMixin, LoginRequiredMixin, ListView): action = request.POST[field] if action == 'D': return HttpResponseRedirect(reverse('import_delete', - kwargs={'pk':imprt.pk})) + kwargs={'pk': imprt.pk})) elif action == 'A': imprt.initialize() elif action == 'I': @@ -981,12 +1031,15 @@ class ImportListView(IshtarMixin, LoginRequiredMixin, ListView): imprt.archive() return HttpResponseRedirect(reverse(self.current_url)) + class ImportOldListView(ImportListView): current_url = 'old_imports' + def get_queryset(self): user = models.IshtarUser.objects.get(pk=self.request.user.pk) - return self.model.objects.filter(user=user, state='AC' - ).order_by('-creation_date') + return self.model.objects.filter( + user=user, state='AC').order_by('-creation_date') + class ImportLinkView(IshtarMixin, LoginRequiredMixin, ModelFormSetView): template_name = 'ishtar/formset.html' @@ -996,12 +1049,13 @@ class ImportLinkView(IshtarMixin, LoginRequiredMixin, ModelFormSetView): form_class = forms.TargetKeyForm def get_queryset(self): - return self.model.objects.filter(is_set=False, - associated_import=self.kwargs['pk']) + return self.model.objects.filter( + is_set=False, associated_import=self.kwargs['pk']) def get_success_url(self): return reverse('current_imports') + class ImportDeleteView(IshtarMixin, LoginRequiredMixin, DeleteView): template_name = 'ishtar/import_delete.html' model = models.Import @@ -1010,6 +1064,7 @@ class ImportDeleteView(IshtarMixin, LoginRequiredMixin, DeleteView): def get_success_url(self): return reverse('current_imports') + class PersonCreate(LoginRequiredMixin, CreateView): model = models.Person form_class = forms.BasePersonForm @@ -1018,6 +1073,7 @@ class PersonCreate(LoginRequiredMixin, CreateView): def get_success_url(self): return reverse('person_edit', args=[self.object.pk]) + class PersonEdit(LoginRequiredMixin, UpdateView): model = models.Person form_class = forms.BasePersonForm @@ -1026,6 +1082,7 @@ class PersonEdit(LoginRequiredMixin, UpdateView): def get_success_url(self): return reverse('person_edit', args=[self.object.pk]) + class OrganizationCreate(LoginRequiredMixin, CreateView): model = models.Organization form_class = forms.BaseOrganizationForm @@ -1038,6 +1095,7 @@ class OrganizationCreate(LoginRequiredMixin, CreateView): kwargs.update({'prefix': self.form_class.form_prefix}) return kwargs + class OrganizationEdit(LoginRequiredMixin, UpdateView): model = models.Organization form_class = forms.BaseOrganizationForm @@ -1049,6 +1107,7 @@ class OrganizationEdit(LoginRequiredMixin, UpdateView): kwargs.update({'prefix': self.form_class.form_prefix}) return kwargs + class OrganizationPersonCreate(LoginRequiredMixin, CreateView): model = models.Person form_class = forms.BaseOrganizationPersonForm @@ -1064,6 +1123,7 @@ class OrganizationPersonCreate(LoginRequiredMixin, CreateView): def get_success_url(self): return reverse('organization_person_edit', args=[self.object.pk]) + class OrganizationPersonEdit(LoginRequiredMixin, UpdateView): model = models.Person form_class = forms.BaseOrganizationPersonForm @@ -1072,7 +1132,7 @@ class OrganizationPersonEdit(LoginRequiredMixin, UpdateView): def get_context_data(self, *args, **kwargs): data = super(OrganizationPersonEdit, self).get_context_data(*args, - **kwargs) + **kwargs) data['relative_label'] = self.relative_label return data diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py index ba7e61e46..efafa38e2 100644 --- a/ishtar_common/widgets.py +++ b/ishtar_common/widgets.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2010-2014 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2010-2015 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> # Copyright (C) 2007 skam <massimo dot scamarcia at gmail.com> # (http://djangosnippets.org/snippets/233/) @@ -21,7 +21,7 @@ from django import forms from django.conf import settings -from django.core.urlresolvers import resolve, reverse +from django.core.urlresolvers import reverse from django.db.models import fields from django.forms import ClearableFileInput from django.forms.widgets import flatatt @@ -44,7 +44,7 @@ class Select2Multiple(forms.SelectMultiple): css = { 'all': ('select2/css/select2.css',) } - js = ('select2/js/select2.min.js', + js = ('select2/js/select2.min.js', 'select2/js/init.js') def render(self, name, value, attrs=None, choices=()): @@ -54,7 +54,8 @@ class Select2Multiple(forms.SelectMultiple): attrs = {} attrs['class'] = klass return super(Select2Multiple, self).render(name, value, attrs, - choices) + choices) + class MultipleAutocompleteField(forms.MultipleChoiceField): def __init__(self, *args, **kwargs): @@ -65,10 +66,10 @@ class MultipleAutocompleteField(forms.MultipleChoiceField): kwargs['choices'] = [] new = kwargs.pop('new') if 'new' in kwargs else None if 'widget' not in kwargs and self.model: - kwargs['widget'] = JQueryAutoComplete(reverse_lazy( - 'autocomplete-'+self.model.__name__.lower()), - associated_model=self.model, new=new, - multiple=True) + kwargs['widget'] = JQueryAutoComplete( + reverse_lazy('autocomplete-'+self.model.__name__.lower()), + associated_model=self.model, new=new, + multiple=True) super(MultipleAutocompleteField, self).__init__(*args, **kwargs) def get_choices(self): @@ -89,17 +90,18 @@ class MultipleAutocompleteField(forms.MultipleChoiceField): val = value value = [] for v in val: - v = unicode(v).strip('[').strip(']' - ).strip('u').strip("'").strip('"') - value += [int(v.strip()) - for v in list(set(v.split(','))) - if v.strip()] + v = unicode(v).strip('[').strip(']')\ + .strip('u').strip("'").strip('"') + value += [int(va.strip()) + for va in list(set(v.split(','))) + if va.strip()] except (TypeError, ValueError): value = [] else: value = [] return super(MultipleAutocompleteField, self).clean(value) + class DeleteWidget(forms.CheckboxInput): def render(self, name, value, attrs=None): final_attrs = flatatt(self.build_attrs(attrs, name=name, @@ -109,28 +111,31 @@ class DeleteWidget(forms.CheckboxInput): output.append('</td></tr>') return mark_safe('\n'.join(output)) + class ImageFileInput(ClearableFileInput): template_with_initial = u'<span class="prettyPhoto">%(initial)s</span>'\ - u' %(clear_template)s<br />%(input_text)s: %(input)s' + u' %(clear_template)s<br />%(input_text)s: %(input)s' + class SquareMeterWidget(forms.TextInput): def render(self, name, value, attrs=None): if not value: value = u"" final_attrs = flatatt(self.build_attrs(attrs, name=name, value=value)) - dct = {'final_attrs':final_attrs, - 'unit':settings.SURFACE_UNIT_LABEL, - 'id':attrs['id'], - "safe_id":attrs['id'].replace('-', '_')} + dct = {'final_attrs': final_attrs, + 'unit': settings.SURFACE_UNIT_LABEL, + 'id': attrs['id'], + "safe_id": attrs['id'].replace('-', '_')} t = loader.get_template('blocks/SquareMeterWidget.html') rendered = t.render(Context(dct)) return mark_safe(rendered) AreaWidget = forms.TextInput + if settings.SURFACE_UNIT == 'square-metre': - #global AreaWidget AreaWidget = SquareMeterWidget + class JQueryDate(forms.TextInput): def __init__(self, *args, **kwargs): super(JQueryDate, self).__init__(*args, **kwargs) @@ -166,12 +171,14 @@ class JQueryDate(forms.TextInput): } $(window).load(load_jquerydate_%(var_name)s); //--></script> -""" % {"name":name, "var_name":var_name, "country":settings.COUNTRY} +""" % {"name": name, "var_name": var_name, "country": settings.COUNTRY} return rendered + class JQueryAutoComplete(forms.TextInput): def __init__(self, source, associated_model=None, options={}, attrs={}, - new=False, multiple=False, limit={}): + new=False, url_new='', multiple=False, limit={}, + dynamic_limit=[]): """ Source can be a list containing the autocomplete values or a string containing the url used for the request. @@ -184,8 +191,10 @@ class JQueryAutoComplete(forms.TextInput): self.options = JSONEncoder().encode(options) self.attrs.update(attrs) self.new = new + self.url_new = url_new self.multiple = multiple self.limit = limit + self.dynamic_limit = dynamic_limit def value_from_datadict(self, data, files, name): if self.multiple: @@ -203,8 +212,14 @@ class JQueryAutoComplete(forms.TextInput): source = "'" + unicode(self.source) + "'" except: raise ValueError('source type is not valid') - dct = {'source':mark_safe(source), - 'field_id':field_id,} + dynamic_limit = [ + 'id_' + lim.replace('_', '') + '-' + + '-'.join(field_id.split('-')[1:-1]) + '-' + lim + for lim in self.dynamic_limit + ] + dct = {'source': mark_safe(source), + 'field_id': field_id, + 'dynamic_limit': dynamic_limit} if self.options: dct['options'] = mark_safe('%s' % self.options) @@ -253,7 +268,7 @@ class JQueryAutoComplete(forms.TextInput): if hiddens and selects: attrs_hidden['value'] = hiddens[0] attrs_select['value'] = selects[0] - if not self.attrs.has_key('id'): + if 'id' not in self.attrs: attrs_hidden['id'] = 'id_%s' % name attrs_select['id'] = 'id_select_%s' % name if 'class' not in attrs_select: @@ -264,11 +279,14 @@ class JQueryAutoComplete(forms.TextInput): limits = [] for k in self.limit: limits.append(k + "__" + "-".join( - [unicode(v) for v in self.limit[k]])) + [unicode(v) for v in self.limit[k]])) args = [attrs_select['id']] if limits: args.append(';'.join(limits)) - url_new = reverse('new-' + model_name, args=args) + url_new = 'new-' + model_name + if self.url_new: + url_new = self.url_new + url_new = reverse(url_new, args=args) new = u' <a href="#" class="add-button" '\ u'onclick="open_window(\'%s\');">+</a>' % url_new html = u'''<input%(attrs_select)s/>%(new)s\ @@ -276,13 +294,14 @@ class JQueryAutoComplete(forms.TextInput): <script type="text/javascript"><!--// $(function() {%(js)s});//--></script> ''' % { - 'attrs_select' : flatatt(attrs_select), - 'attrs_hidden' : flatatt(attrs_hidden), - 'js' : self.render_js(name), - 'new':new + 'attrs_select': flatatt(attrs_select), + 'attrs_hidden': flatatt(attrs_hidden), + 'js': self.render_js(name), + 'new': new } return html + class JQueryTown(forms.TextInput): """ Town fields whith state and department pre-selections @@ -305,7 +324,7 @@ class JQueryTown(forms.TextInput): if isinstance(source, list): encoded_src = JSONEncoder().encode(source) elif isinstance(source, str) \ - or isinstance(source, unicode): + or isinstance(source, unicode): src = escape(source) if not src.endswith('/'): src += "/" @@ -357,31 +376,32 @@ class JQueryTown(forms.TextInput): if hiddens and selects: attrs_hidden['value'] = hiddens[0] attrs_select['value'] = selects[0] - if not self.attrs.has_key('id'): + if 'id' not in self.attrs: attrs_hidden['id'] = 'id_%s' % name attrs_select['id'] = 'id_select_%s' % name if 'class' not in attrs_select: attrs_select['class'] = 'autocomplete' source = self.encode_source(self.source) - dct = {'source':mark_safe(source), - 'selected':selected, - 'safe_field_id':slugify(name).replace('-', '_'), - 'field_id':name} + dct = {'source': mark_safe(source), + 'selected': selected, + 'safe_field_id': slugify(name).replace('-', '_'), + 'field_id': name} if self.options: dct['options'] = mark_safe('%s' % self.options) - dct.update({'attrs_select':mark_safe(flatatt(attrs_select)), - 'attrs_hidden':mark_safe(flatatt(attrs_hidden)), - 'name':name, - 'states':models.State.objects.all().order_by('label'), - 'selected_department':selected_department, - 'selected_state':selected_state - }) - html = loader.get_template('blocks/JQueryAdvancedTown.html').render( - Context(dct)) + dct.update({'attrs_select': mark_safe(flatatt(attrs_select)), + 'attrs_hidden': mark_safe(flatatt(attrs_hidden)), + 'name': name, + 'states': models.State.objects.all().order_by('label'), + 'selected_department': selected_department, + 'selected_state': selected_state} + ) + html = loader.get_template('blocks/JQueryAdvancedTown.html')\ + .render(Context(dct)) return html + class JQueryPersonOrganization(forms.TextInput): """ Complex widget which manage: @@ -392,7 +412,7 @@ class JQueryPersonOrganization(forms.TextInput): def __init__(self, source, edit_source, model, options={}, attrs={}, new=False, limit={}, - html_template = 'blocks/PersonOrganization.html', + html_template='blocks/PersonOrganization.html', js_template='blocks/JQueryPersonOrganization.js'): self.options = None self.attrs = {} @@ -413,7 +433,7 @@ class JQueryPersonOrganization(forms.TextInput): if isinstance(source, list): encoded_src = JSONEncoder().encode(source) elif isinstance(source, str) \ - or isinstance(source, unicode): + or isinstance(source, unicode): encoded_src = "'%s'" % escape(source) else: try: @@ -425,11 +445,11 @@ class JQueryPersonOrganization(forms.TextInput): def render_js(self, field_id, selected=''): source = self.encode_source(self.source) edit_source = self.encode_source(self.edit_source) - dct = {'source':mark_safe(source), - 'edit_source':mark_safe(edit_source), - 'selected':selected, - 'safe_field_id':slugify(field_id).replace('-', '_'), - 'field_id':field_id} + dct = {'source': mark_safe(source), + 'edit_source': mark_safe(edit_source), + 'selected': selected, + 'safe_field_id': slugify(field_id).replace('-', '_'), + 'field_id': field_id} if self.options: dct['options'] = mark_safe('%s' % self.options) js = loader.get_template(self.js_template).render(Context(dct)) @@ -467,33 +487,33 @@ class JQueryPersonOrganization(forms.TextInput): if hiddens and selects: attrs_hidden['value'] = hiddens[0] attrs_select['value'] = selects[0] - if not self.attrs.has_key('id'): + if 'id' not in self.attrs: attrs_hidden['id'] = 'id_%s' % name attrs_select['id'] = 'id_select_%s' % name if 'class' not in attrs_select: attrs_select['class'] = 'autocomplete' new = '' - dct = {'attrs_select':mark_safe(flatatt(attrs_select)), - 'attrs_hidden':mark_safe(flatatt(attrs_hidden)), - 'name':name, - 'js':self.render_js(name, selected), - 'new':mark_safe(new)} + dct = {'attrs_select': mark_safe(flatatt(attrs_select)), + 'attrs_hidden': mark_safe(flatatt(attrs_hidden)), + 'name': name, + 'js': self.render_js(name, selected), + 'new': mark_safe(new)} html = loader.get_template(self.html_template).render(Context(dct)) return html + class JQueryJqGrid(forms.RadioSelect): COL_TPL = "{name:'%(idx)s', index:'%(idx)s', sortable:true}" + class Media: js = ['%s/js/i18n/grid.locale-%s.js' % (settings.STATIC_URL, settings.COUNTRY), - '%s/js/jquery.jqGrid.min.js' % settings.STATIC_URL, - ] - css = {'all':['%s/media/ui.jqgrid.css' % settings.STATIC_URL, - ]} + '%s/js/jquery.jqGrid.min.js' % settings.STATIC_URL] + css = {'all': ['%s/media/ui.jqgrid.css' % settings.STATIC_URL]} def __init__(self, source, form, associated_model, attrs={}, - table_cols='TABLE_COLS', multiple=False, multiple_cols=[2], new=False, - new_message="", source_full=None): + table_cols='TABLE_COLS', multiple=False, multiple_cols=[2], + new=False, new_message="", source_full=None): self.source = source self.form = form self.attrs = attrs @@ -507,7 +527,7 @@ class JQueryJqGrid(forms.RadioSelect): def render(self, name, value=None, attrs=None): t = loader.get_template('blocks/form_snippet.html') form = self.form() - rendered = t.render(Context({'form':form})) + rendered = t.render(Context({'form': form})) dct = {} if self.new: model_name = self.associated_model._meta.object_name.lower() @@ -516,7 +536,6 @@ class JQueryJqGrid(forms.RadioSelect): extra_cols = [] col_names, col_idx = [], [] for k in form.get_input_ids(): - #field = form.fields[k] col_idx.append(u'"%s"' % k) for field_name in getattr(self.associated_model, self.table_cols): field = self.associated_model @@ -536,7 +555,7 @@ class JQueryJqGrid(forms.RadioSelect): else: continue col_names.append(u'"%s"' % field_verbose_name) - extra_cols.append(self.COL_TPL % {'idx':field_name}) + extra_cols.append(self.COL_TPL % {'idx': field_name}) col_names = col_names and ", ".join(col_names) or "" col_idx = col_idx and ", ".join(col_idx) or "" extra_cols = extra_cols and ", ".join(extra_cols) or "" @@ -544,19 +563,18 @@ class JQueryJqGrid(forms.RadioSelect): dct['source'] = unicode(self.source) if unicode(self.source_full) and unicode(self.source_full) != 'None': dct['source_full'] = unicode(self.source_full) - dct.update({'name':name, - 'col_names':col_names, - 'extra_cols':extra_cols, - 'source':unicode(self.source), - 'col_idx':col_idx, - 'no_result':unicode(_("No results")), - 'loading':unicode(_("Loading...")), - 'remove':unicode(_(u"Remove")), - 'sname':name.replace('-', ''), - 'multiple':self.multiple, - 'multi_cols': ",".join((u'"%d"' % col \ - for col in self.multiple_cols)) - }) + dct.update({'name': name, + 'col_names': col_names, + 'extra_cols': extra_cols, + 'source': unicode(self.source), + 'col_idx': col_idx, + 'no_result': unicode(_("No results")), + 'loading': unicode(_("Loading...")), + 'remove': unicode(_(u"Remove")), + 'sname': name.replace('-', ''), + 'multiple': self.multiple, + 'multi_cols': ",".join((u'"%d"' % col + for col in self.multiple_cols))}) t = loader.get_template('blocks/JQueryJqGrid.html') rendered += t.render(Context(dct)) return mark_safe(rendered) diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py index af236504c..7da654b80 100644 --- a/ishtar_common/wizards.py +++ b/ishtar_common/wizards.py @@ -23,13 +23,15 @@ from django.conf import settings from django.contrib.formtools.wizard.storage import get_storage from django.contrib.formtools.wizard.views import NamedUrlWizardView, \ normalize_name +from django.contrib.sites.models import Site from django.core.exceptions import ObjectDoesNotExist from django.core.files.images import ImageFile +from django.core.mail import send_mail from django.db.models.fields.files import FileField from django.db.models.fields.related import ManyToManyField from django.forms import ValidationError from django.shortcuts import render_to_response -from django.template import RequestContext +from django.template import Context, RequestContext, loader from django.utils.datastructures import MultiValueDict as BaseMultiValueDict from django.utils.translation import ugettext_lazy as _ import models @@ -216,12 +218,13 @@ class Wizard(NamedUrlWizardView): if form_datas: form_datas.append(("", "", "spacer")) items = hasattr(base_form, 'fields') and \ - base_form.fields.keyOrder or cleaned_data.keys() + 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: + 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: @@ -229,22 +232,22 @@ class Wizard(NamedUrlWizardView): if not lbl: continue value = cleaned_data[key] - if not value and value != False: + if value is None or value == '': continue if key in self.translated_keys: value = _(value) if type(value) == bool: - if value == True: + if value: value = _(u"Yes") - elif value == False: + else: value = _(u"No") elif key in associated_models: values = [] if type(value) in (tuple, list): values = value elif "," in unicode(value): - values = unicode(value - ).strip('[').strip(']').split(",") + values = unicode( + value).strip('[').strip(']').split(",") else: values = [value] rendered_values = [] @@ -274,7 +277,7 @@ class Wizard(NamedUrlWizardView): 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 {} + base_form.associated_models or {} if hasattr(form, 'forms'): multi = False if form.forms: @@ -289,7 +292,8 @@ class Wizard(NamedUrlWizardView): multi = len(fields) > 1 if multi: assert hasattr(frm, 'base_model'), \ - u"Must define a base_model for " + unicode(frm.__class__) + u"Must define a base_model for " + \ + unicode(frm.__class__) for frm in form.forms: if not frm.is_valid(): continue @@ -300,10 +304,11 @@ class Wizard(NamedUrlWizardView): frm.cleaned_data.pop('DELETE') for key in frm.cleaned_data: value = frm.cleaned_data[key] - if not value and value != False: + if value is None or value == '': continue if key in associated_models: - value = associated_models[key].objects.get(pk=value) + value = associated_models[key].objects.get( + pk=value) if multi: vals[key] = value else: @@ -319,10 +324,10 @@ class Wizard(NamedUrlWizardView): if value: model = associated_models[key] if isinstance(value, unicode) \ - or isinstance(value, str) and "," in value: + or isinstance(value, str) and "," in value: value = value.split(",") if isinstance(value, list) \ - or isinstance(value, tuple): + or isinstance(value, tuple): value = [model.objects.get(pk=val) for val in value if val] if len(value) == 1: @@ -346,11 +351,11 @@ class Wizard(NamedUrlWizardView): return_object) def get_saved_model(self): - """Permit a distinguo when saved model is not the base selected model""" + "Permit a distinguo when saved model is not the base selected model" return self.model def get_current_saved_object(self): - """Permit a distinguo when saved model is not the base selected model""" + "Permit a distinguo when saved model is not the base selected model" return self.get_current_object() def save_model(self, dct, m2m, whole_associated_models, form_list, @@ -363,7 +368,8 @@ class Wizard(NamedUrlWizardView): if '__' not in k: continue vals = k.split('__') - assert len(vals) == 2, "Only one level of dependant item is managed" + 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] = {} @@ -388,7 +394,7 @@ class Wizard(NamedUrlWizardView): setattr(obj, k, dct[k]) try: obj.full_clean() - except ValidationError, msg: + except ValidationError: return self.render(form_list[-1]) for dependant_item in other_objs: c_item = getattr(obj, dependant_item) @@ -429,7 +435,7 @@ class Wizard(NamedUrlWizardView): obj = self.get_saved_model()(**dct) try: obj.full_clean() - except ValidationError, msg: + except ValidationError: return self.render(form_list[-1]) obj.save() for k in adds: @@ -463,13 +469,13 @@ class Wizard(NamedUrlWizardView): # necessary to manage interaction between models like # material_index management for baseitems obj.save() - dct = {'item':obj} + dct = {'item': obj} # force evaluation of lazy urls wizard_done_window = unicode(self.wizard_done_window) if wizard_done_window: dct['wizard_done_window'] = wizard_done_window res = render_to_response(self.wizard_done_template, dct, - context_instance=RequestContext(self.request)) + context_instance=RequestContext(self.request)) return return_object and (obj, res) or res def get_deleted(self, keys): @@ -494,8 +500,6 @@ class Wizard(NamedUrlWizardView): def get_form(self, step=None, data=None, files=None): """Manage formset""" - request = self.request - storage = self.storage if data: data = data.copy() if not step: @@ -513,7 +517,7 @@ class Wizard(NamedUrlWizardView): if to_delete: # reorganize for idx, number in enumerate(sorted(not_to_delete, - key=lambda x:int(x))): + key=lambda x: int(x))): idx = unicode(idx) if idx == number: continue @@ -527,8 +531,8 @@ class Wizard(NamedUrlWizardView): frm = form.form if callable(frm): frm = frm() - required_fields = [k for k in frm.fields - if frm.fields[k].required] + required_fields = [ki for ki in frm.fields + if frm.fields[ki].required] base_key = None if required_fields: base_key = required_fields[-1] @@ -538,8 +542,8 @@ class Wizard(NamedUrlWizardView): total_field = 0 if base_key: total_field = len([key for key in data.keys() - if base_key in key.split('-') - and data[key]]) + if base_key in key.split('-') + and data[key]]) if init and not to_delete and ( not hasattr(self, 'form_initialized') or not self.form_initialized): @@ -548,7 +552,8 @@ class Wizard(NamedUrlWizardView): data[step + u'-TOTAL_FORMS'] = unicode(total_field + 1) # TODO:remove form_initialized? # update initialization - #if request.POST and init and hasattr(self, 'form_initialized') \ + # if request.POST and init and hasattr(self, + # 'form_initialized') \ # and self.form_initialized: # for k in init[0]: # data[step + '-' + unicode(total_field) + '-' + k] = \ @@ -560,15 +565,15 @@ class Wizard(NamedUrlWizardView): if hasattr(form, 'fields') and form.fields.keys(): frm = form elif hasattr(form, 'extra_form') and hasattr(form.extra_form, 'fields')\ - and form.extra_form.fields.keys(): + and form.extra_form.fields.keys(): frm = form.extra_form elif hasattr(form, 'forms') and form.forms \ - and form.forms[0].fields.keys(): + and form.forms[0].fields.keys(): frm = form.forms[0] if frm: first_field = frm.fields[frm.fields.keyOrder[0]] attrs = first_field.widget.attrs - attrs.update({'autofocus':"autofocus"}) + attrs.update({'autofocus': "autofocus"}) first_field.widget.attrs = attrs return form @@ -584,10 +589,11 @@ class Wizard(NamedUrlWizardView): 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']: + elif 'validate_and_end' in request.POST \ + and request.POST['validate_and_end']: last_step = self.steps.last - new_form = self.get_form(last_step, + 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 @@ -668,9 +674,8 @@ class Wizard(NamedUrlWizardView): 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]: - val = request.session[storage.prefix]['step_data']\ - [form_key][k] + request.session[storage.prefix]['step_data'][form_key][k]: + val = request.session[storage.prefix]['step_data'][form_key][k] if type(val) in (list, tuple): val = val[0] vals.append(val) @@ -694,13 +699,13 @@ class Wizard(NamedUrlWizardView): 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() + model_name = self.form_list[step]\ + .associated_models['pk'].__name__.lower() if step == current_step: self.storage.reset() val = model_name in request.session and request.session[model_name] if val: - return MultiValueDict({'pk':val}) + return MultiValueDict({'pk': val}) elif current_obj: return self.get_instanced_init(current_obj, step) current_form = self.form_list[current_step] @@ -709,7 +714,7 @@ class Wizard(NamedUrlWizardView): 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] + request.session[model_name] if val: initial[key] = val if initial: @@ -729,8 +734,8 @@ class Wizard(NamedUrlWizardView): self.request.session[obj_name] = unicode(obj.pk) initial = MultiValueDict() if self.request.POST or \ - (step in self.request.session[self.storage.prefix] and\ - self.request.session[self.storage.prefix]['step_data'][step]): + (step in self.request.session[self.storage.prefix] and + self.request.session[self.storage.prefix]['step_data'][step]): return initial if hasattr(c_form, 'base_fields'): for base_field in c_form.base_fields.keys(): @@ -738,13 +743,13 @@ class Wizard(NamedUrlWizardView): if hasattr(c_form, 'base_model') and \ base_field == c_form.base_model: key = c_form.base_model + 's' - initial.setlist(base_field, [unicode(val.pk) - for val in getattr(obj, key).all()]) + initial.setlist(base_field, [ + unicode(val.pk) for val in getattr(obj, key).all()]) else: fields = base_field.split('__') for field in fields: if not hasattr(value, field) or \ - getattr(value, field) == None: + getattr(value, field) is None: value = obj break value = getattr(value, field) @@ -762,7 +767,7 @@ class Wizard(NamedUrlWizardView): isinstance(value, FileField) or \ isinstance(value, ImageFile): initial[base_field] = value - elif value != None: + elif value is not None: initial[base_field] = unicode(value) elif hasattr(c_form, 'management_form'): initial = [] @@ -789,7 +794,7 @@ class Wizard(NamedUrlWizardView): value = getattr(child_obj, field) if hasattr(value, 'pk'): value = value.pk - if value != None: + if value is not None: vals[field] = unicode(value) if vals: initial.append(vals) @@ -799,8 +804,9 @@ class Wizard(NamedUrlWizardView): class SearchWizard(NamedUrlWizardView): model = None label = '' - modification = None # True when the wizard modify an item - storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage' + modification = None # True when the wizard modify an item + storage_name = \ + 'django.contrib.formtools.wizard.storage.session.SessionStorage' def get_wizard_name(self): """ @@ -812,7 +818,7 @@ class SearchWizard(NamedUrlWizardView): """As the class name can interfere when reused prefix with the url_name """ return self.url_name + super(SearchWizard, self).get_prefix(*args, - **kwargs) + **kwargs) def get_template_names(self): templates = ['ishtar/wizard/search.html'] @@ -822,8 +828,8 @@ class SearchWizard(NamedUrlWizardView): context = super(SearchWizard, self).get_context_data(form) self.request.session['CURRENT_ACTION'] = self.get_wizard_name() current_step = self.steps.current - context.update({'current_step':self.form_list[current_step], - 'wizard_label':self.label}) + context.update({'current_step': self.form_list[current_step], + 'wizard_label': self.label}) return context @@ -843,7 +849,8 @@ class DeletionWizard(Wizard): 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']) + self.current_obj = model.objects.get( + pk=form.cleaned_data['pk']) if not self.current_obj: return datas res = {} @@ -873,8 +880,9 @@ class DeletionWizard(Wizard): obj.delete() except ObjectDoesNotExist: pass - return render_to_response('ishtar/wizard/wizard_delete_done.html', {}, - context_instance=RequestContext(self.request)) + return render_to_response( + 'ishtar/wizard/wizard_delete_done.html', {}, + context_instance=RequestContext(self.request)) class ClosingWizard(Wizard): @@ -893,7 +901,7 @@ class ClosingWizard(Wizard): if key == 'pk': model = form.associated_models['pk'] self.current_obj = model.objects.get( - pk=form.cleaned_data['pk']) + pk=form.cleaned_data['pk']) if not self.current_obj: return datas res = {} @@ -921,11 +929,13 @@ class ClosingWizard(Wizard): obj = self.get_current_object() for form in form_list: if form.is_valid(): - if 'end_date' in form.cleaned_data and hasattr(obj, 'end_date'): + 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('ishtar/wizard/wizard_closing_done.html', {}, - context_instance=RequestContext(self.request)) + return render_to_response( + 'ishtar/wizard/wizard_closing_done.html', {}, + context_instance=RequestContext(self.request)) class PersonWizard(Wizard): @@ -940,7 +950,7 @@ class PersonDeletionWizard(DeletionWizard): model = models.Person fields = model.TABLE_COLS wizard_templates = { - 'final-person_deletion':'ishtar/wizard/wizard_person_deletion.html'} + 'final-person_deletion': 'ishtar/wizard/wizard_person_deletion.html'} class OrganizationWizard(Wizard): @@ -955,12 +965,13 @@ class OrganizationDeletionWizard(DeletionWizard): model = models.Organization fields = model.TABLE_COLS wizard_templates = { - 'final-organization_deletion':\ - 'ishtar/wizard/wizard_organization_deletion.html'} + 'final-organization_deletion': + 'ishtar/wizard/wizard_organization_deletion.html'} class AccountWizard(Wizard): model = models.Person + def get_formated_datas(self, forms): datas = super(AccountWizard, self).get_formated_datas(forms) for form in forms: @@ -980,7 +991,7 @@ class AccountWizard(Wizard): if not form.is_valid(): return self.render(form) associated_models = hasattr(form, 'associated_models') and \ - form.associated_models or {} + form.associated_models or {} if type(form.cleaned_data) == dict: for key in form.cleaned_data: if key == 'pk': @@ -1001,10 +1012,11 @@ class AccountWizard(Wizard): 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) + 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() @@ -1014,20 +1026,21 @@ class AccountWizard(Wizard): 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 "" - }) + 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} + "app_name": app_name} send_mail(subject, msg, settings.ADMINS[0][1], [dct['email']], fail_silently=True) res = render_to_response('ishtar/wizard/wizard_done.html', {}, - context_instance=RequestContext(self.request)) + context_instance=RequestContext(self.request)) return res def get_form(self, step=None, data=None, files=None): @@ -1045,6 +1058,7 @@ class AccountWizard(Wizard): class SourceWizard(Wizard): model = None + def get_extra_model(self, dct, form_list): dct = super(SourceWizard, self).get_extra_model(dct, form_list) if 'history_modifier' in dct: |