# HG changeset patch # User Paul Boddie # Date 1329655133 -3600 # Node ID a5aa61c0be15fac1583756b0195d53b1e1a7283b # Parent 75990ca1e4c667a7cc54b0574c60a85ec043c62b Added sort column controls in table headers. Separated the sort criteria acquired from the request and from the region so that the column types can be fully specified in the region and retrieved regardless of the request's criteria. diff -r 75990ca1e4c6 -r a5aa61c0be15 ImprovedTableParser.py --- a/ImprovedTableParser.py Sun Feb 19 00:54:13 2012 +0100 +++ b/ImprovedTableParser.py Sun Feb 19 13:38:53 2012 +0100 @@ -280,32 +280,44 @@ ascending = False # Extract the conversion indicator and column index. - - if column_spec.endswith("n"): - column = column_spec[:-1] - fn = to_number - else: - column = column_spec - fn = str - # Ignore badly-specified columns. try: + column = get_number(column_spec) + suffix = column_spec[len(column):] + fn = converters[suffix] sort_columns.append((max(0, int(column) - start), fn, ascending)) except ValueError: pass return sort_columns +def get_column_types(sort_columns): + + """ + Return a dictionary mapping column indexes to conversion functions. + """ + + d = {} + for column, fn, ascending in sort_columns: + d[column] = fn, ascending + return d + +def get_number(s): + + "From 's', get any leading number." + + match = leading_number_regexp.match(s) + if match: + return match.group() + else: + return "" + def to_number(s): "Convert 's' to a number, discarding any non-numeric trailing data." - match = leading_number_regexp.match(s) - if match: - return int(match.group()) - else: - raise ValueError, s + return int(get_number(s)) class Sorter: @@ -342,6 +354,80 @@ return 0 +def write_sort_control(columnnumber, write, sort_columns, column_types, columns, table_name, data_start, start=0): + + """ + Write a sort control in its own form which provides a list of sort + descriptions, modifying the 'sort_columns' provided by introducing the given + column in different positions. + """ + + option_html = """\ + +""" + + # Start with the existing criteria without this column being involved. + + current_sort_columns = [(column + start, suffixes[fn], not ascending and "d" or "") + for (column, fn, ascending) in sort_columns] + revised_sort_columns = [(column + start, suffixes[fn], not ascending and "d" or "") + for (column, fn, ascending) in sort_columns if column != columnnumber] + values = [revised_sort_columns] + revised_sort_labels = [columns[column][1].strip() for (column, fn, ascending) in revised_sort_columns] + labels = [revised_sort_labels] + + # Add this column in all possible places in the sorting criteria. + + i = 0 + while i <= len(revised_sort_columns): + value = revised_sort_columns[:] + label = revised_sort_labels[:] + fn, ascending = column_types.get(columnnumber, (str, True)) + value.insert(i, (columnnumber + start, suffixes[fn], not ascending and "d" or "")) + label.insert(i, columns[columnnumber][1].strip()) + values.append(value) + labels.append(label) + i += 1 + + # Make the list of options. + + options_html = [] + for value, label in zip(values, labels): + options_html.append(option_html % { + "value" : ",".join([("%d%s%s" % spec) for spec in value]), + "label" : ", ".join(label), + "selected" : value == current_sort_columns and 'selected="selected"' or "", + }) + + # Write the form. + + d = { + "table_name" : table_name, + "options" : "".join(options_html), + "data_start" : data_start, + } + + write("""\ +
+ + + +
+""" % d) + +# Sorting-related tables. + +converters = { + "n" : to_number, + "" : str, + } + +suffixes = {} +for key, value in converters.items(): + suffixes[value] = key + # Common formatting functions. def formatTable(text, request, fmt, attrs=None): @@ -358,36 +444,53 @@ # Override any region arguments with request parameters. table_name = attrs.get("name") - sortcolumns = table_name and getQualifiedParameter(request, table_name, "sortcolumns") or attrs.get("sortcolumns") + + # Get sorting criteria from the region and the request. + + region_sortcolumns = attrs.get("sortcolumns") + sortcolumns = table_name and getQualifiedParameter(request, table_name, "sortcolumns") or region_sortcolumns # Sort the rows according to the values in each of the specified columns. + data_start = int(table_name and getQualifiedParameter(request, table_name, "headers") or attrs.get("headers", "1")) + if sortcolumns: - data_start = int(attrs.get("headers", "1")) headers = table[:data_start] data = table[data_start:] # Get the sort columns using Unix sort-like notation. - sorter = Sorter(get_sort_columns(sortcolumns)) + sort_columns = get_sort_columns(sortcolumns) + region_sort_columns = get_sort_columns(region_sortcolumns) + + sorter = Sorter(sort_columns) data.sort(cmp=sorter) table = headers + data + column_types = get_column_types(region_sort_columns) # Write the table. - request.write(fmt.table(1, table_attrs)) + writing_html = request.page.output_mimetype == "text/html" + write = request.write + write(fmt.table(1, table_attrs)) - for row_attrs, columns in table: - request.write(fmt.table_row(1, row_attrs)) + for rownumber, (row_attrs, columns) in enumerate(table): + write(fmt.table_row(1, row_attrs)) + + for columnnumber, (column_attrs, column_text) in enumerate(columns): + write(fmt.table_cell(1, column_attrs)) + write(formatText(column_text, request, fmt)) - for column_attrs, column_text in columns: - request.write(fmt.table_cell(1, column_attrs)) - request.write(formatText(column_text, request, fmt)) - request.write(fmt.table_cell(0)) + # Add sorting controls, if appropriate. + + if writing_html and sortcolumns and rownumber == data_start - 1: + write_sort_control(columnnumber, write, sort_columns, column_types, columns, table_name, data_start) - request.write(fmt.table_row(0)) + write(fmt.table_cell(0)) - request.write(fmt.table(0)) + write(fmt.table_row(0)) + + write(fmt.table(0)) # vim: tabstop=4 expandtab shiftwidth=4