From 3015f3e7a26b4ec3f0f3d887966941917c1e1fb9 Mon Sep 17 00:00:00 2001 From: Yoshinori Okuji <yo@nexedi.com> Date: Fri, 16 Jul 2004 17:10:37 +0000 Subject: [PATCH] common review JPS/YO. Addition of stat_columns code. Incomplete url_columns. Filtering empty string params now applies to all methods except objectValues. Renaming to selection_expression (instead of stat_query). git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@1215 20353a03-c40f-0410-a6d1-a30d3c3de9de --- product/ERP5Form/ListBox.py | 168 ++++++++++++++++++++++++------------ 1 file changed, 113 insertions(+), 55 deletions(-) diff --git a/product/ERP5Form/ListBox.py b/product/ERP5Form/ListBox.py index 52604e901a..b414d30d1a 100755 --- a/product/ERP5Form/ListBox.py +++ b/product/ERP5Form/ListBox.py @@ -26,7 +26,7 @@ # ############################################################################## -import string +import string, types, sys from Products.Formulator.DummyField import fields from Products.Formulator import Widget, Validator from Products.Formulator.Field import ZMIField @@ -136,7 +136,7 @@ class ListBoxWidget(Widget.Widget): """ property_names = Widget.Widget.property_names +\ ['lines', 'columns', 'all_columns', 'search_columns', 'sort_columns', 'sort', - 'editable_columns', 'all_editable_columns', 'global_attributes', + 'editable_columns', 'all_editable_columns', 'stat_columns', 'global_attributes', 'list_method', 'stat_method', 'selection_name', 'meta_types', 'portal_types', 'default_params', 'search', 'select', @@ -261,6 +261,20 @@ class ListBoxWidget(Widget.Widget): default=[], required=0) + stat_columns = fields.ListTextAreaField('stat_columns', + title="Stat Columns", + description=( + "An optional list of columns which can be used for statistics."), + default=[], + required=0) + + url_columns = fields.ListTextAreaField('url_columns', + title="URL Columns", + description=( + "An optional list of columns which can provide a custom URL."), + default=[], + required=0) + global_attributes = fields.ListTextAreaField('global_attributes', title="Global Attributes", description=( @@ -326,6 +340,7 @@ class ListBoxWidget(Widget.Widget): sort = field.get_value('sort') editable_columns = field.get_value('editable_columns') all_editable_columns = field.get_value('all_editable_columns') + stat_columns = field.get_value('stat_columns') search_columns = field.get_value('search_columns') sort_columns = field.get_value('sort_columns') domain_tree = field.get_value('domain_tree') @@ -355,12 +370,17 @@ class ListBoxWidget(Widget.Widget): sort_columns = search_columns sort_columns_id_list = map(lambda x: x[0], sort_columns) - # We only display stats if a stat button exsists - # XXXXXXXXXXXX This is not necessarily a good - # idea since we may want to hardcode stats in certain cases + # Display statistics if the button Parameter exists or stat columns are defined explicitly. filtered_actions = here.portal_actions.listFilteredActionsFor(here); object_ui = filtered_actions.has_key('object_ui') - show_stat = object_ui + show_stat = (object_ui or stat_columns) + + # If nothing is specified to stat_columns, assume that all columns are available. + # For compatibility, because there was no stat_columns before. + if not stat_columns: + stat_columns = [] + for column in all_columns: + stat_columns.append((column[0], column[0])) has_catalog_path = None for (k, v) in all_columns: @@ -487,12 +507,28 @@ class ListBoxWidget(Widget.Widget): # Execute the query kw = params + + # XXX Remove selection_expression if present. + # This is necessary for now, because the actual selection expression in + # search catalog does not take the requested columns into account. If + # select_expression is passed, this can raise an exception, because stat + # method sets select_expression, and this might cause duplicated column + # names. + # + # In the future, this must be addressed in a clean way. selection_expression + # should be used for search catalog, and search catalog should not use + # catalog.* but only selection_expression. But this is a bit difficult, + # because there is no simple way to distinguish queried columns from callable + # objects in the current ListBox configuration. + if 'select_expression' in kw: + del kw['select_expression'] + if hasattr(list_method, 'method_name'): if list_method.method_name == 'objectValues': list_method = here.objectValues kw = copy(params) kw['spec'] = filtered_meta_types - elif list_method.method_name == 'portal_catalog': + else: # The Catalog Builds a Complex Query # So we should not pass too many variables kw = {} @@ -512,28 +548,16 @@ class ListBoxWidget(Widget.Widget): for cname in params.keys(): if params[cname] != '' and params[cname]!=None: kw[cname] = params[cname] - elif list_method in (None, ''): # Use current selection - # Use previously used list method - list_method = None - else: - # Include portal_type selection - if REQUEST.form.has_key('portal_type'): - kw['portal_type'] = REQUEST.form['portal_type'] - elif REQUEST.has_key('portal_type'): - kw['portal_type'] = REQUEST['portal_type'] - elif filtered_portal_types is not None: - kw['portal_type'] = filtered_portal_types - elif filtered_meta_types is not None: - kw['meta_type'] = filtered_meta_types - elif kw.has_key('portal_type'): - if kw['portal_type'] == '': - del kw['portal_type'] # Try to get the method through acquisition try: list_method = getattr(here, list_method.method_name) except: pass + elif list_method in (None, ''): # Use current selection + # Use previously used list method + list_method = None + # Lookup the stat_method if hasattr(stat_method, 'method_name'): @@ -554,26 +578,7 @@ class ListBoxWidget(Widget.Widget): else: stat_method = here.portal_catalog.countResults - # Build the stat query part which is common to each query of this script - if show_stat: - stats = here.portal_selections.getSelectionStats(selection_name, REQUEST=REQUEST) - stat_query = '' - index = 0 - - for (k,v) in columns: - try: - if stats[index] != ' ': - stat_query += stats[index] + '(' + k + ') AS ' + k + ',' - else: - stat_query += '\' \' AS ' + k + ',' - except: - stat_query += '\' \' AS ' + k + ',' - index = index + 1 - - stat_query = stat_query[:len(stat_query) - 1] - kw['stat_query'] = stat_query - selection.edit( params = kw ) - + LOG('ListBox', 0, 'domain_tree = %s, selection.getSelectionDomainPath() = %s, selection.getSelectionDomainList() = %s' % (repr(domain_tree), repr(selection.getSelectionDomainPath()), repr(selection.getSelectionDomainList()))) if domain_tree: selection_domain_path = selection.getSelectionDomainPath() selection_domain_current = selection.getSelectionDomainList() @@ -586,6 +591,8 @@ class ListBoxWidget(Widget.Widget): pass + #LOG('ListBox', 0, 'list_method = %s, list_method.__dict__ = %s' % (repr(list_method), repr((list_method.__dict__)))) + # When we build the body, we have to go through # All report lines # This is made as a list of tuples @@ -670,7 +677,7 @@ class ListBoxWidget(Widget.Widget): report_sections = ( (None, 0, 0, object_list, len(object_list), 0), ) object_uid_list = map(lambda x: getattr(x, 'uid', None), object_list) - LOG('ListBox.render, object_uid_list:',0,object_uid_list) + #LOG('ListBox.render, object_uid_list:',0,object_uid_list) # Then construct the md5 corresponding this uid list # It is used in order to do some checks in scripts. # For example, if we do delete objects, then we do have a list of @@ -712,10 +719,10 @@ class ListBoxWidget(Widget.Widget): if list_method is not None: try: method_path = getPath(here) + '/' + list_method.method_name - LOG('ListBox', 0, 'method_path = %s, getPath = %s, list_method.method_name = %s' % (repr(method_path), repr(getPath(here)), repr(list_method.method_name))) + #LOG('ListBox', 0, 'method_path = %s, getPath = %s, list_method.method_name = %s' % (repr(method_path), repr(getPath(here)), repr(list_method.method_name))) except: method_path = getPath(here) + '/' + list_method.__name__ - LOG('ListBox', 0, 'method_path = %s, getPath = %s, list_method.__name__ = %s' % (repr(method_path), repr(getPath(here)), repr(list_method.__name__))) + #LOG('ListBox', 0, 'method_path = %s, getPath = %s, list_method.__name__ = %s' % (repr(method_path), repr(getPath(here)), repr(list_method.__name__))) # Sometimes the seltion name is a list ??? Why ???? if type(current_selection_name) in (type(()),type([])): current_selection_name = current_selection_name[0] @@ -1046,9 +1053,14 @@ onChange="submitAction(this.form,'%s/portal_selections/setReportRoot')"> attribute_value = 'Object does not exist' if callable(attribute_value): try: - attribute_value = attribute_value() + try: + attribute_value = attribute_value(brain = o, selection = selection) + except TypeError: + attribute_value = attribute_value() except: + LOG('ListBox', 0, '', error=sys.exc_info()) attribute_value = "Could not evaluate" + #LOG('ListBox', 0, 'o = %s' % repr(dir(o))) if type(attribute_value) is type(0.0): if sql in editable_column_ids and form.has_field('%s_%s' % (field.id, alias) ): # Do not truncate if editable @@ -1106,6 +1118,31 @@ onChange="submitAction(this.form,'%s/portal_selections/setReportRoot')"> # Call the stat_method if show_stat: + stats = here.portal_selections.getSelectionStats(selection_name, REQUEST=REQUEST) + select_expression = '' + index = 0 + + for (sql,title,alias) in extended_columns: + # XXX This might be slow. + for column in stat_columns: + if column[0] == sql: + break + else: + column = None + if column is not None and column[0] == column[1]: + try: + if stats[index] != ' ': + select_expression += stats[index] + '(' + sql + ') AS ' + alias + ',' + else: + select_expression += '\' \' AS ' + alias + ',' + except: + select_expression += '\' \' AS ' + alias + ',' + index = index + 1 + + select_expression = select_expression[:len(select_expression) - 1] + kw['select_expression'] = select_expression + selection.edit( params = kw ) + count_results = selection(selection_method = stat_method, context=here, REQUEST=REQUEST) list_body = list_body + '<tr>' @@ -1115,13 +1152,34 @@ onChange="submitAction(this.form,'%s/portal_selections/setReportRoot')"> list_body += '<td class="Data"> </td>' for n in range((len(extended_columns))): try: - alias = extended_columns[n][2] - value = getattr(count_results[0],alias,'') - if callable(value): value=value() - if type(value) is type(1.0): - list_body += '<td class="Data" align="right">%.2f</td>' % value + sql = extended_columns[n][0] + for column in stat_columns: + if column[0] == sql: + break + else: + column = None + #LOG('ListBox', 0, 'n = %s, extended_columns = %s, stat_columns = %s, column = %s' % (repr(n), repr(extended_columns), repr(stat_columns), repr(column))) + if column is not None: + if column[0] == column[1]: + alias = extended_columns[n][2] + value = getattr(count_results[0],alias,'') + else: + value = getattr(here, column[1]) + #LOG('ListBox', 0, 'column = %s, value = %s' % (repr(column), repr(value))) + if callable(value): + try: + params = dict(kw) + #params['operator'] = stats[n] + value=value(**params) + except: + LOG('ListBox', 0, 'WARNING: Could not call %s with %s: ' % (repr(value), repr(params)), error=sys.exc_info()) + pass + if type(value) is type(1.0): + list_body += '<td class="Data" align="right">%.2f</td>' % value + else: + list_body += '<td class="Data">' + str(value) + '</td>' else: - list_body += '<td class="Data">' + str(value) + '</td>' + list_body += '<td class="Data"> </td>' except: list_body += '<td class="Data"> </td>' list_body += '</tr>' @@ -1217,8 +1275,8 @@ class ListBoxValidator(Validator.Validator): o = newTempBase(portal, uid[4:]) # Arghhh - XXX acquisition problem - use portal root o.uid = uid listbox[uid[4:]] = {} - # We first try to set a listbox corresponding to all things - # we can validate, so that we can use the same list + # We first try to set a listbox corresponding to all things + # we can validate, so that we can use the same list # as the one used for displaying the listbox for sql in editable_column_ids: alias = '_'.join(sql.split('.')) -- 2.30.9