diff options
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/forms.py | 4 | ||||
-rw-r--r-- | ishtar_common/locale/django.pot | 126 | ||||
-rw-r--r-- | ishtar_common/models.py | 13 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/wizard/confirm_wizard.html | 4 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/wizard/default_wizard.html | 7 | ||||
-rw-r--r-- | ishtar_common/templatetags/window_tables.py | 12 | ||||
-rw-r--r-- | ishtar_common/tests.py | 31 | ||||
-rw-r--r-- | ishtar_common/views.py | 35 | ||||
-rw-r--r-- | ishtar_common/widgets.py | 47 | ||||
-rw-r--r-- | ishtar_common/wizards.py | 63 |
10 files changed, 236 insertions, 106 deletions
diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py index 043b03f61..42d74f9ef 100644 --- a/ishtar_common/forms.py +++ b/ishtar_common/forms.py @@ -33,6 +33,7 @@ from django.utils.translation import ugettext_lazy as _ import models import widgets +from wizards import MultiValueDict # from formwizard.forms import NamedUrlSessionFormWizard @@ -224,7 +225,6 @@ class ManageOldType(object): if prefix not in k: continue new_k = k[len(prefix) + 1:] - items = [] if hasattr(kwargs['data'], 'getlist'): items = kwargs['data'].getlist(k) else: @@ -238,7 +238,6 @@ class ManageOldType(object): if 'initial' in kwargs and kwargs['initial']: for k in kwargs['initial']: if k not in self.init_data or not self.init_data[k]: - items = [] if hasattr(kwargs['initial'], 'getlist'): items = kwargs['initial'].getlist(k) else: @@ -249,6 +248,7 @@ class ManageOldType(object): if k not in self.init_data: self.init_data[k] = [] self.init_data[k].append(val) + self.init_data = MultiValueDict(self.init_data) super(ManageOldType, self).__init__(*args, **kwargs) diff --git a/ishtar_common/locale/django.pot b/ishtar_common/locale/django.pot index e08650308..813f31a3c 100644 --- a/ishtar_common/locale/django.pot +++ b/ishtar_common/locale/django.pot @@ -160,7 +160,7 @@ msgid "Template" msgstr "" #: forms_common.py:41 forms_common.py:59 forms_common.py:182 -#: forms_common.py:406 models.py:1426 models.py:2819 +#: forms_common.py:406 models.py:1426 models.py:2818 #: templates/blocks/JQueryAdvancedTown.html:19 #: templates/ishtar/sheet_organization.html:13 msgid "Town" @@ -177,7 +177,7 @@ msgid "" msgstr "" #: forms_common.py:68 forms_common.py:855 ishtar_menu.py:47 models.py:1599 -#: models.py:2445 models.py:2627 models.py:2689 +#: models.py:2445 models.py:2627 models.py:2688 #: templates/ishtar/sheet_person.html:4 msgid "Person" msgstr "" @@ -196,7 +196,7 @@ msgstr "" #: forms_common.py:173 forms_common.py:210 forms_common.py:322 #: forms_common.py:376 forms_common.py:446 models.py:1027 models.py:1359 -#: models.py:1645 models.py:1864 models.py:2325 models.py:2431 models.py:2805 +#: models.py:1645 models.py:1864 models.py:2325 models.py:2431 models.py:2804 #: templates/ishtar/sheet_organization.html:8 #: templates/ishtar/sheet_organization.html:21 msgid "Name" @@ -245,7 +245,7 @@ msgid "Mobile phone" msgstr "" #: forms_common.py:211 forms_common.py:325 forms_common.py:449 models.py:2025 -#: models.py:2327 models.py:2740 templates/sheet_ope.html:85 +#: models.py:2327 models.py:2739 templates/sheet_ope.html:85 #: templates/sheet_ope.html.py:105 templates/sheet_ope.html:126 #: templates/ishtar/import_list.html:13 #: templates/ishtar/sheet_organization.html:23 @@ -288,7 +288,7 @@ msgid "Identity" msgstr "" #: forms_common.py:371 forms_common.py:773 forms_common.py:822 models.py:1996 -#: models.py:2423 models.py:2425 models.py:2737 templates/sheet_ope.html:104 +#: models.py:2423 models.py:2425 models.py:2736 templates/sheet_ope.html:104 #: templates/ishtar/blocks/window_tables/documents.html:7 msgid "Title" msgstr "" @@ -365,7 +365,7 @@ msgstr "" msgid "Account" msgstr "" -#: forms_common.py:575 wizards.py:1194 +#: forms_common.py:575 wizards.py:1217 msgid "New password" msgstr "" @@ -389,7 +389,7 @@ msgstr "" msgid "Send the new password by email?" msgstr "" -#: forms_common.py:628 forms_common.py:641 models.py:2820 +#: forms_common.py:628 forms_common.py:641 models.py:2819 msgid "Towns" msgstr "" @@ -405,7 +405,7 @@ msgstr "" msgid "Documentation informations" msgstr "" -#: forms_common.py:775 forms_common.py:823 models.py:1997 models.py:2714 +#: forms_common.py:775 forms_common.py:823 models.py:1997 models.py:2713 msgid "Source type" msgstr "" @@ -417,37 +417,37 @@ msgstr "" msgid "Internal reference" msgstr "" -#: forms_common.py:783 models.py:2751 +#: forms_common.py:783 models.py:2750 msgid "Numerical ressource (web address)" msgstr "" -#: forms_common.py:784 models.py:2753 +#: forms_common.py:784 models.py:2752 msgid "Receipt date" msgstr "" -#: forms_common.py:786 models.py:2154 models.py:2755 +#: forms_common.py:786 models.py:2154 models.py:2754 msgid "Creation date" msgstr "" -#: forms_common.py:789 models.py:2758 +#: forms_common.py:789 models.py:2757 msgid "Receipt date in documentation" msgstr "" #: forms_common.py:791 forms_common.py:827 models.py:322 models.py:627 -#: models.py:1891 models.py:2437 models.py:2765 +#: models.py:1891 models.py:2437 models.py:2764 msgid "Comment" msgstr "" #: forms_common.py:793 forms_common.py:826 models.py:1029 models.py:1649 -#: models.py:1823 models.py:1865 models.py:2764 templates/sheet_ope.html:128 +#: models.py:1823 models.py:1865 models.py:2763 templates/sheet_ope.html:128 msgid "Description" msgstr "" -#: forms_common.py:796 models.py:2766 +#: forms_common.py:796 models.py:2765 msgid "Additional information" msgstr "" -#: forms_common.py:798 forms_common.py:830 models.py:2768 +#: forms_common.py:798 forms_common.py:830 models.py:2767 msgid "Has a duplicate" msgstr "" @@ -462,7 +462,7 @@ msgid "" "p>" msgstr "" -#: forms_common.py:819 forms_common.py:848 forms_common.py:882 models.py:2694 +#: forms_common.py:819 forms_common.py:848 forms_common.py:882 models.py:2693 #: templates/ishtar/wizard/wizard_person_deletion.html:124 msgid "Author" msgstr "" @@ -475,7 +475,7 @@ msgstr "" msgid "Would you like to delete this documentation?" msgstr "" -#: forms_common.py:856 models.py:1998 models.py:2682 models.py:2691 +#: forms_common.py:856 models.py:1998 models.py:2681 models.py:2690 msgid "Author type" msgstr "" @@ -487,7 +487,7 @@ msgstr "" msgid "There are identical authors." msgstr "" -#: forms_common.py:893 models.py:2695 models.py:2747 +#: forms_common.py:893 models.py:2694 models.py:2746 #: templates/sheet_ope.html:106 #: templates/ishtar/blocks/window_tables/documents.html:9 msgid "Authors" @@ -505,7 +505,7 @@ msgstr "" msgid "Deletion" msgstr "" -#: ishtar_menu.py:39 models.py:1155 views.py:1501 +#: ishtar_menu.py:39 models.py:1155 views.py:1513 msgid "Global variables" msgstr "" @@ -537,11 +537,11 @@ msgstr "" msgid "Imports" msgstr "" -#: ishtar_menu.py:112 views.py:1509 +#: ishtar_menu.py:112 views.py:1521 msgid "New import" msgstr "" -#: ishtar_menu.py:116 views.py:1523 +#: ishtar_menu.py:116 views.py:1535 msgid "Current imports" msgstr "" @@ -589,7 +589,7 @@ msgstr "" msgid "Creator" msgstr "" -#: models.py:891 models.py:2831 +#: models.py:891 models.py:2830 msgid "Order" msgstr "" @@ -834,11 +834,11 @@ msgstr "" msgid "Operation source" msgstr "" -#: models.py:1609 views.py:1317 views.py:1367 +#: models.py:1609 views.py:1329 views.py:1379 msgid "Archaeological files" msgstr "" -#: models.py:1611 views.py:1320 views.py:1375 +#: models.py:1611 views.py:1332 views.py:1387 msgid "Context records" msgstr "" @@ -970,11 +970,11 @@ msgstr "" msgid "Importer - Targets keys" msgstr "" -#: models.py:1999 models.py:2730 models.py:2743 +#: models.py:1999 models.py:2729 models.py:2742 msgid "Format" msgstr "" -#: models.py:2000 models.py:2835 +#: models.py:2000 models.py:2834 msgid "Operation type" msgstr "" @@ -1022,7 +1022,7 @@ msgstr "" msgid "Context record relation type" msgstr "" -#: models.py:2015 models.py:2722 +#: models.py:2015 models.py:2721 msgid "Support type" msgstr "" @@ -1300,71 +1300,71 @@ msgstr "" msgid "Ishtar users" msgstr "" -#: models.py:2677 +#: models.py:2676 msgid "To modify the password use the form in Auth > User" msgstr "" -#: models.py:2683 +#: models.py:2682 msgid "Author types" msgstr "" -#: models.py:2715 +#: models.py:2714 msgid "Source types" msgstr "" -#: models.py:2723 +#: models.py:2722 msgid "Support types" msgstr "" -#: models.py:2731 +#: models.py:2730 msgid "Formats" msgstr "" -#: models.py:2738 +#: models.py:2737 msgid "External ID" msgstr "" -#: models.py:2741 +#: models.py:2740 msgid "Support" msgstr "" -#: models.py:2745 +#: models.py:2744 msgid "Scale" msgstr "" -#: models.py:2759 +#: models.py:2758 msgid "Item number" msgstr "" -#: models.py:2760 +#: models.py:2759 msgid "Ref." msgstr "" -#: models.py:2763 +#: models.py:2762 msgid "Internal ref." msgstr "" -#: models.py:2806 +#: models.py:2805 msgid "Surface (m2)" msgstr "" -#: models.py:2807 templates/sheet_ope.html:46 templates/sheet_ope.html.py:107 +#: models.py:2806 templates/sheet_ope.html:46 templates/sheet_ope.html.py:107 msgid "Localisation" msgstr "" -#: models.py:2832 +#: models.py:2831 msgid "Is preventive" msgstr "" -#: models.py:2836 +#: models.py:2835 msgid "Operation types" msgstr "" -#: models.py:2865 +#: models.py:2864 msgid "Preventive" msgstr "" -#: models.py:2866 +#: models.py:2865 msgid "Research" msgstr "" @@ -1420,76 +1420,76 @@ msgstr "" msgid "Find" msgstr "" -#: views.py:1256 views.py:1299 +#: views.py:1268 views.py:1311 msgid "Operation not permitted." msgstr "" -#: views.py:1258 +#: views.py:1270 #, python-format msgid "New %s" msgstr "" -#: views.py:1318 views.py:1371 +#: views.py:1330 views.py:1383 msgid "Operations" msgstr "" -#: views.py:1322 views.py:1378 +#: views.py:1334 views.py:1390 msgid "Finds" msgstr "" -#: views.py:1570 templates/ishtar/import_list.html:43 +#: views.py:1582 templates/ishtar/import_list.html:43 msgid "Link unmatched items" msgstr "" -#: views.py:1585 +#: views.py:1597 msgid "Delete import" msgstr "" -#: views.py:1624 +#: views.py:1636 msgid "Merge persons" msgstr "" -#: views.py:1648 +#: views.py:1660 msgid "Select the main person" msgstr "" -#: views.py:1657 +#: views.py:1669 msgid "Merge organization" msgstr "" -#: views.py:1667 +#: views.py:1679 msgid "Select the main organization" msgstr "" -#: views.py:1707 views.py:1723 +#: views.py:1719 views.py:1735 msgid "Corporation manager" msgstr "" -#: widgets.py:239 widgets.py:347 widgets.py:462 +#: widgets.py:239 widgets.py:346 widgets.py:461 msgid "Search..." msgstr "" -#: widgets.py:642 templatetags/window_tables.py:79 +#: widgets.py:643 templatetags/window_tables.py:81 msgid "No results" msgstr "" -#: widgets.py:643 templatetags/window_tables.py:80 +#: widgets.py:644 templatetags/window_tables.py:82 msgid "Loading..." msgstr "" -#: widgets.py:644 +#: widgets.py:645 msgid "Remove" msgstr "" -#: wizards.py:323 templates/ishtar/import_delete.html:20 +#: wizards.py:335 templates/ishtar/import_delete.html:20 msgid "Yes" msgstr "" -#: wizards.py:325 +#: wizards.py:337 msgid "No" msgstr "" -#: wizards.py:1251 +#: wizards.py:1274 #, python-format msgid "[%(app_name)s] Account creation/modification" msgstr "" diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 5b822c7dd..d4f0c595e 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -413,10 +413,12 @@ class GeneralType(Cached, models.Model): new_vals = [] if not initial: return [] + if type(initial) not in (list, tuple): + initial = [initial] for value in initial: try: pk = int(value) - except ValueError: + except (ValueError, TypeError): continue if pk in type_pks: continue @@ -447,6 +449,7 @@ class GeneralType(Cached, models.Model): keys = ['__get_types'] keys += [u"{}".format(ex) for ex in exclude] + [ empty_first and 'empty_first' or ''] + [u"{}".format(default)] + keys += [u'{}-{}'.format(unicode(k), dct[k]) for k in dct] cache_key, value = get_cache(cls, keys) if value and not force: return value @@ -485,7 +488,10 @@ class GeneralType(Cached, models.Model): pass items = cls.objects.filter(**dct) if default and default != "None": - exclude.append(default.txt_idx) + if hasattr(default, 'txt_idx'): + exclude.append(default.txt_idx) + else: + exclude.append(default) if exclude: items = items.exclude(txt_idx__in=exclude) for item in items.order_by(*cls._meta.ordering).all(): @@ -664,7 +670,7 @@ class ImageModel(models.Model): null=True, max_length=255) IMAGE_MAX_SIZE = settings.IMAGE_MAX_SIZE THUMB_MAX_SIZE = settings.THUMB_MAX_SIZE - IMAGE_PREFIX = '/' + IMAGE_PREFIX = '' class Meta: abstract = True @@ -2639,7 +2645,6 @@ class IshtarUser(User): surname = user.first_name or default name = user.last_name or default email = user.email - person_type = None if user.is_superuser: ADMINISTRATOR, created = PersonType.objects.get_or_create( txt_idx='administrator') diff --git a/ishtar_common/templates/ishtar/wizard/confirm_wizard.html b/ishtar_common/templates/ishtar/wizard/confirm_wizard.html index 9007c867c..1fbaadcd1 100644 --- a/ishtar_common/templates/ishtar/wizard/confirm_wizard.html +++ b/ishtar_common/templates/ishtar/wizard/confirm_wizard.html @@ -6,9 +6,9 @@ <form action="." method="post">{% csrf_token %} <ul id='form_path'> {% for step in previous_steps %} - <li><button name="form_prev_step" value="{{forloop.counter0}}">{{step.form_label}}</button></li> + <li><button name="form_prev_step" value="{{forloop.counter0}}">{{step}}</button></li> {% endfor %} - <li class='current'><a href='#'>{{current_step.form_label}}</a></li> + <li class='current'><a href='#'>{{current_step_label}}</a></li> </ul> </form> <form action="." method="post">{% csrf_token %} diff --git a/ishtar_common/templates/ishtar/wizard/default_wizard.html b/ishtar_common/templates/ishtar/wizard/default_wizard.html index c52d71536..fad1895bd 100644 --- a/ishtar_common/templates/ishtar/wizard/default_wizard.html +++ b/ishtar_common/templates/ishtar/wizard/default_wizard.html @@ -10,11 +10,11 @@ <form action="." method="post">{% csrf_token %} <ul id='form_path'> {% for step in previous_steps %} - <li><button class='change_step' name="form_prev_step" value="{{forloop.counter0}}">{{step.form_label}}</button></li> + <li><button class='change_step' name="form_prev_step" value="{{forloop.counter0}}">{{step}}</button></li> {% endfor %} - <li class='current'><a href='#'>{{current_step.form_label}}</a></li> + <li class='current'><a href='#'>{{current_step_label}}</a></li> {% for step in next_steps %} - <li><button class='change_step' name="form_prev_step" value="{{forloop.counter|add:previous_step_counter}}">{{step.form_label}}</button></li> + <li><button class='change_step' name="form_prev_step" value="{{forloop.counter|add:previous_step_counter}}">{{step}}</button></li> {% endfor %} </ul> </form> @@ -24,6 +24,7 @@ <div class='form'> {{ wizard.form.media }} {{ wizard.management_form }} +{% block form_head %}{% endblock %} {% block form_detail %} {% if wizard.form.forms %} {{ wizard.form.management_form }} diff --git a/ishtar_common/templatetags/window_tables.py b/ishtar_common/templatetags/window_tables.py index acb3d4a0a..377b6e435 100644 --- a/ishtar_common/templatetags/window_tables.py +++ b/ishtar_common/templatetags/window_tables.py @@ -17,7 +17,7 @@ from archaeological_operations.models import OperationSource, Operation from archaeological_context_records.models import ContextRecord, \ ContextRecordSource, RecordRelations as CRRecordRelations from archaeological_finds.models import Find, FindSource, \ - FindUpstreamTreatments, FindDownstreamTreatments + FindUpstreamTreatments, FindDownstreamTreatments, FindTreatments register = template.Library() @@ -45,22 +45,28 @@ ASSOCIATED_MODELS['context_records_relations'] = ( ASSOCIATED_MODELS['finds'] = (Find, 'get-find', 'get-find-full') ASSOCIATED_MODELS['finds_for_ope'] = ( Find, 'get-find-for-ope', 'get-find-full') +ASSOCIATED_MODELS['finds_for_treatment'] = ( + Find, 'get-find-for-treatment', 'get-find-full') ASSOCIATED_MODELS['finds_docs'] = ( FindSource, 'get-findsource', 'get-findsource-full') ASSOCIATED_MODELS['finds_upstreamtreatments'] = ( FindUpstreamTreatments, 'get-upstreamtreatment', '') ASSOCIATED_MODELS['finds_downstreamtreatments'] = ( FindDownstreamTreatments, 'get-downstreamtreatment', '') +ASSOCIATED_MODELS['treatments'] = ( + FindTreatments, 'get-treatment', '') @register.simple_tag(takes_context=True) def dynamic_table_document( context, caption, associated_model, key, value, - table_cols='TABLE_COLS', output='html', large=False): + table_cols='TABLE_COLS', output='html', large=False, + col_prefix=''): if not table_cols: table_cols = 'TABLE_COLS' model, url, url_full = ASSOCIATED_MODELS[associated_model] - grid = JQueryJqGrid(None, None, model, table_cols=table_cols) + grid = JQueryJqGrid(None, None, model, table_cols=table_cols, + col_prefix=col_prefix) source = unicode(reverse_lazy(url)) source_full = unicode(reverse_lazy(url_full)) if url_full else '' source_attrs = mark_safe('?submited=1&{}={}'.format(key, value)) diff --git a/ishtar_common/tests.py b/ishtar_common/tests.py index 0c4bbda08..dbe3df4a5 100644 --- a/ishtar_common/tests.py +++ b/ishtar_common/tests.py @@ -75,6 +75,32 @@ def create_user(): return username, password, user +class WizardTestFormData(object): + """ + Test set to simulate wizard steps + """ + def __init__(self, name, form_datas, ignored=[], extra_tests=[]): + """ + :param name: explicit name of the test + :param form_datas: dict with data for each step - dict key are wizard + step name + :param ignored: steps to be ignored in wizard processing + :param extra_tests: list of extra tests. Theses tests must be functions + accepting two parameters: the current test object and the final step + response + """ + self.form_datas = form_datas + self.ignored = ignored[:] + self.extra_tests = extra_tests + + def tests(self, test_object, final_step_response): + """ + Specific tests for theses datas. Raise Exception if not OK. + """ + for test in self.extra_tests: + test(test_object, final_step_response) + + class WizardTest(object): url_name = None wizard_name = '' @@ -95,7 +121,9 @@ class WizardTest(object): def test_wizard(self): url = reverse(self.url_name) self.pre_wizard() - for form_data, ignored in self.form_datas: + for test_form_data in self.form_datas: + form_data = test_form_data.form_datas + ignored = test_form_data.ignored for idx, step in enumerate(self.steps): current_step, current_form = step if current_step in ignored: @@ -138,6 +166,7 @@ class WizardTest(object): '/{}/{}'.format(self.url_name, next_form)) else: response = self.client.post(url, data, follow=True) + test_form_data.tests(self, response) self.post_wizard() diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 8634125cd..6426fef8f 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -528,7 +528,7 @@ HIERARCHIC_FIELDS = ['periods', 'period', 'unit', 'material_types', def get_item(model, func_name, default_name, extra_request_keys=[], - base_request={}, bool_fields=[], reversed_bool_fields=[], + base_request=None, bool_fields=[], reversed_bool_fields=[], dated_fields=[], associated_models=[], relative_session_names=[], specific_perms=[], own_table_cols=None, relation_types_prefix={}): """ @@ -577,10 +577,12 @@ def get_item(model, func_name, default_name, extra_request_keys=[], my_extra_request_keys = copy(model.EXTRA_REQUEST_KEYS) else: my_extra_request_keys = copy(extra_request_keys) - if not base_request and hasattr(model, 'BASE_REQUEST'): + if base_request is None and hasattr(model, 'BASE_REQUEST'): my_base_request = copy(model.BASE_REQUEST) - else: + elif base_request is not None: my_base_request = copy(base_request) + else: + my_base_request = {} if not bool_fields and hasattr(model, 'BOOL_FIELDS'): my_bool_fields = model.BOOL_FIELDS[:] else: @@ -677,7 +679,7 @@ def get_item(model, func_name, default_name, extra_request_keys=[], request.session[default_name].split('-')[-1]} else: dct = {"pk": request.session[default_name]} - elif dct == base_request: + elif dct == (base_request or {}): # a parent item may be selected in the default menu for name, key in my_relative_session_names: if name in request.session and request.session[name]: @@ -910,6 +912,14 @@ def get_item(model, func_name, default_name, extra_request_keys=[], keys = [keys] my_vals = [] for k in keys: + if hasattr(model, 'EXTRA_REQUEST_KEYS') \ + and k in model.EXTRA_REQUEST_KEYS: + k = model.EXTRA_REQUEST_KEYS[k] + if type(k) in (list, tuple): + k = k[0] + for filtr in ('__icontains', '__contains'): + if k.endswith(filtr): + k = k[:len(k) - len(filtr)] vals = [item] # foreign key may be divided by "." or "__" splitted_k = [] @@ -929,10 +939,14 @@ def get_item(model, func_name, default_name, extra_request_keys=[], v = v() new_vals.append(v) elif val: - val = getattr(val, ky) - if callable(val): - val = val() - new_vals.append(val) + try: + val = getattr(val, ky) + if callable(val): + val = val() + new_vals.append(val) + except AttributeError: + # must be a query key such as "contains" + pass vals = new_vals # manage last related objects if vals and hasattr(vals[0], 'all'): @@ -995,9 +1009,9 @@ def get_item(model, func_name, default_name, extra_request_keys=[], # foreign key may be divided by "." or "__" for tc in table_col: if '.' in tc: - tab_cols.append(tc.split('.')[-1]) + tab_cols += tc.split('.') elif '__' in tc: - tab_cols.append(tc.split('__')[-1]) + tab_cols += tc.split('__') else: tab_cols.append(tc) k = "__".join(tab_cols) @@ -1118,7 +1132,6 @@ def show_item(model, name, extra_dct=None): context_instance = RequestContext(request) context_instance.update(dct) context_instance['output'] = 'html' - filename = "" if hasattr(item, 'history_object'): filename = item.history_object.associated_filename else: diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py index 1183836bc..be0ab8cba 100644 --- a/ishtar_common/widgets.py +++ b/ishtar_common/widgets.py @@ -19,12 +19,15 @@ # See the file COPYING for details. +import logging + from django import forms from django.conf import settings -from django.core.urlresolvers import reverse +from django.core.urlresolvers import reverse, NoReverseMatch from django.db.models import fields from django.forms import ClearableFileInput -from django.forms.widgets import flatatt +from django.forms.widgets import flatatt, \ + CheckboxSelectMultiple as CheckboxSelectMultipleBase from django.template import Context, loader from django.template.defaultfilters import slugify from django.utils.encoding import smart_unicode @@ -36,6 +39,8 @@ from django.utils.translation import ugettext_lazy as _ from ishtar_common import models +logger = logging.getLogger(__name__) + reverse_lazy = lazy(reverse, unicode) @@ -56,6 +61,20 @@ class Select2Multiple(forms.SelectMultiple): return super(Select2Multiple, self).render(name, value, attrs, choices) +class CheckboxSelectMultiple(CheckboxSelectMultipleBase): + """ + Fix initialization bug. + Should be corrected on recent Django version. + TODO: test and remove (test case: treatment type not keep on modif) + """ + def render(self, name, value, attrs=None, choices=()): + if type(value) in (str, unicode): + value = value.split(',') + if type(value) not in (list, tuple): + value = [value] + return super(CheckboxSelectMultiple, self).render(name, value, attrs, + choices) + class MultipleAutocompleteField(forms.MultipleChoiceField): def __init__(self, *args, **kwargs): @@ -322,7 +341,6 @@ class JQueryTown(forms.TextInput): @classmethod def encode_source(cls, source): - encoded_src = '' if isinstance(source, list): encoded_src = JSONEncoder().encode(source) elif isinstance(source, str) \ @@ -515,7 +533,8 @@ class JQueryJqGrid(forms.RadioSelect): def __init__(self, source, form, associated_model, attrs={}, table_cols='TABLE_COLS', multiple=False, multiple_cols=[2], new=False, new_message="", source_full=None, - multiple_select=False, sortname="__default__"): + multiple_select=False, sortname="__default__", + col_prefix=''): """ JQueryJqGrid widget init. @@ -531,6 +550,7 @@ class JQueryJqGrid(forms.RadioSelect): :param source_full: url to get full listing :param multiple_select: :param sortname: column name (model attribute) to use to sort + :param col_prefix: prefix to remove to col_names """ super(JQueryJqGrid, self).__init__(attrs=attrs) self.source = source @@ -546,6 +566,9 @@ class JQueryJqGrid(forms.RadioSelect): self.new, self.new_message = new, new_message self.source_full = source_full self.sortname = sortname + self.col_prefix = col_prefix + if self.col_prefix and not self.col_prefix.endswith('__'): + self.col_prefix += "__" def get_cols(self, python=False): jq_col_names, extra_cols = [], [] @@ -563,24 +586,26 @@ class JQueryJqGrid(forms.RadioSelect): keys = col_name.split('__') if '.' in col_name: keys = col_name.split('.') - f_name = '' for key in keys: if hasattr(field, 'rel') and field.rel: field = field.rel.to try: field = field._meta.get_field(key) field_verbose_name = field.verbose_name - f_name = field.name except (fields.FieldDoesNotExist, AttributeError): if hasattr(field, key + '_lbl'): - f_name = key field_verbose_name = getattr(field, key + '_lbl') else: continue if field_name: field_name += "__" - field_name += f_name + if col_name.startswith(self.col_prefix): + field_name += col_name[len(self.col_prefix):] + else: + field_name += col_name field_verbose_names.append(unicode(field_verbose_name)) + if not field_name: + field_name = "__".join(col_names) if field_name in col_labels: jq_col_names.append(unicode(col_labels[field_name])) elif col_names and col_names[0] in col_labels: @@ -618,7 +643,11 @@ class JQueryJqGrid(forms.RadioSelect): col_idx = col_idx and ", ".join(col_idx) or "" dct['encoding'] = settings.ENCODING or 'utf-8' - dct['source'] = unicode(self.source) + try: + dct['source'] = unicode(self.source) + except NoReverseMatch: + logger.warning('Cannot resolve source for {} widget'.format( + self.form)) if unicode(self.source_full) and unicode(self.source_full) != 'None': dct['source_full'] = unicode(self.source_full) diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py index c065459f6..2fbe30e0e 100644 --- a/ishtar_common/wizards.py +++ b/ishtar_common/wizards.py @@ -18,6 +18,7 @@ # See the file COPYING for details. import datetime +import logging # from functools import wraps from django.conf import settings @@ -38,6 +39,8 @@ from django.utils.datastructures import MultiValueDict as BaseMultiValueDict from django.utils.translation import ugettext_lazy as _ import models +logger = logging.getLogger(__name__) + class MultiValueDict(BaseMultiValueDict): def get(self, *args, **kwargs): @@ -126,6 +129,8 @@ class Wizard(NamedUrlWizardView): current_obj_slug = '' file_storage = default_storage + saved_args = {} # argument to pass on object save + ''' # buggy and unecessary... def __init__(self, *args, **kwargs): @@ -165,6 +170,13 @@ class Wizard(NamedUrlWizardView): return super(Wizard, self).dispatch(request, *args, **kwargs) + def get_form_kwargs(self, step=None): + kwargs = super(Wizard, self).get_form_kwargs(step) + if hasattr(self.form_list[step], 'need_user_for_initialization') and\ + self.form_list[step].need_user_for_initialization: + kwargs['user'] = self.request.user + return kwargs + def get_prefix(self, *args, **kwargs): """As the class name can interfere when reused prefix with the url_name """ @@ -194,7 +206,7 @@ class Wizard(NamedUrlWizardView): self.request.session['CURRENT_ACTION'] = self.get_wizard_name() step = self.steps.first current_step = self.steps.current - dct = {'current_step': self.form_list[current_step], + dct = {'current_step_label': self.form_list[current_step].form_label, 'wizard_label': self.label, 'current_object': self.get_current_object(), 'is_search': current_step.startswith('selec-') @@ -209,7 +221,7 @@ class Wizard(NamedUrlWizardView): or (previous_steps and previous_steps[-1] == self.form_list[step]): break - previous_steps.append(self.form_list[step]) + previous_steps.append(self.form_list[step].form_label) previous_step_counter += 1 if previous_step_counter >= len(self.steps): break @@ -250,7 +262,7 @@ class Wizard(NamedUrlWizardView): if step == next_step: current_step_passed = True elif current_step_passed: - next_steps.append(self.form_list[next_step]) + next_steps.append(self.form_list[next_step].form_label) next_step = self.get_next_step(next_step) context.update({'next_steps': next_steps}) # not last step: validation @@ -483,9 +495,12 @@ class Wizard(NamedUrlWizardView): elif type(dct[k]) not in (list, tuple): dct[k] = [dct[k]] setattr(obj, k, dct[k]) + if hasattr(obj, 'pre_save'): + obj.pre_save() try: obj.full_clean() - except ValidationError: + except ValidationError as e: + logger.warning(unicode(e)) return self.render(form_list[-1]) for dependant_item in other_objs: c_item = getattr(obj, dependant_item) @@ -543,12 +558,19 @@ class Wizard(NamedUrlWizardView): dct[dependant_item] = c_item if 'pk' in dct: dct.pop('pk') + saved_args = self.saved_args.copy() + for k in saved_args: + if k in dct: + saved_args[k] = dct.pop(k) obj = self.get_saved_model()(**dct) + if hasattr(obj, 'pre_save'): + obj.pre_save() try: obj.full_clean() - except ValidationError: + except ValidationError as e: + logger.warning(unicode(e)) return self.render(form_list[-1]) - obj.save() + obj.save(**saved_args) for k in adds: getattr(obj, k).add(adds[k]) # necessary to manage interaction between models like @@ -561,6 +583,12 @@ class Wizard(NamedUrlWizardView): old_m2ms = {} for model in whole_associated_models: related_model = getattr(obj, model + 's') + # manage through + if hasattr(related_model, 'through') and related_model.through: + related_set_name = str( + related_model.through.__name__ + '_set').lower() + if hasattr(obj, related_set_name): + related_model = getattr(obj, related_set_name) # clear real m2m if hasattr(related_model, 'clear'): old_m2ms[model] = [] @@ -587,6 +615,9 @@ class Wizard(NamedUrlWizardView): if value not in m2m_items[key]: if type(value) == dict: model = related_model.model + if hasattr(related_model, 'through') and \ + related_model.through: + model = related_model.through # not m2m -> foreign key if not hasattr(related_model, 'clear'): assert hasattr(model, 'MAIN_ATTR'), \ @@ -611,6 +642,11 @@ class Wizard(NamedUrlWizardView): else: if issubclass(model, models.BaseHistorizedItem): value['history_modifier'] = self.request.user + if hasattr(model, 'RELATIVE_MODELS') and \ + self.get_saved_model() in \ + model.RELATIVE_MODELS: + value[model.RELATIVE_MODELS[ + self.get_saved_model()]] = obj value = model.objects.create(**value) value.save() # check that an item is not add multiple times (forged forms) @@ -626,6 +662,7 @@ class Wizard(NamedUrlWizardView): self.request.session[self.current_obj_slug] = unicode(obj.pk) self.request.session[self.get_object_name(obj)] = unicode(obj.pk) dct = {'item': obj} + self.current_object = obj # force evaluation of lazy urls wizard_done_window = unicode(self.wizard_done_window) if wizard_done_window: @@ -980,8 +1017,18 @@ class Wizard(NamedUrlWizardView): if not hasattr(obj, key): return initial keys = c_form.form.base_fields.keys() - query = getattr(obj, key) - if not obj._meta.ordering: + related = getattr(obj, key) + # manage through + through = False + if hasattr(related, 'through') and related.through: + related_set_name = str( + related.through.__name__ + '_set').lower() + if hasattr(obj, related_set_name): + through = True + related = getattr(obj, related_set_name) + + query = related + if not through and not obj._meta.ordering: query = query.order_by('pk') for child_obj in query.all(): if not keys: |