Author: bugman Date: Fri Mar 8 18:21:37 2013 New Revision: 18714 URL: http://svn.gna.org/viewcvs/relax?rev=18714&view=rev Log: Merged revisions 18709-18713 via svnmerge from svn+ssh://bugman@xxxxxxxxxxx/svn/relax/trunk ........ r18709 | bugman | 2013-03-08 16:09:49 +0100 (Fri, 08 Mar 2013) | 3 lines Added column number checks for the data input into lib.text.table.format_table(). ........ r18710 | bugman | 2013-03-08 17:28:30 +0100 (Fri, 08 Mar 2013) | 6 lines Created the test_format_table6() unit test for lib.text.table.format_table(). This test shows a problem with more than one multi-column cells defined, as well as problems when a multi-column cell is wider than the sum of the widths of the columns it spans. ........ r18711 | bugman | 2013-03-08 17:30:20 +0100 (Fri, 08 Mar 2013) | 6 lines Fix for lib.text.table.format_table() when more than one multi-column cell per row is encountered. The algorithm for determining the total width of the multi-column cell in _table_line() was not checking if the end of the span was being reached. ........ r18712 | bugman | 2013-03-08 18:15:27 +0100 (Fri, 08 Mar 2013) | 5 lines Fix for the test_format_table6() unit test of lib.text.table.format_table(). The numbers of the last column were not properly right justified. ........ r18713 | bugman | 2013-03-08 18:17:45 +0100 (Fri, 08 Mar 2013) | 7 lines The lib.text.table.format_table() function now handles overfull multi-column cells. The _determine_widths() private function has been created to better handle the determination of the table column widths. It will now extend the width of the last column to allow overfull multi-column cells to fit. ........ Modified: branches/frame_order_testing/ (props changed) branches/frame_order_testing/lib/text/table.py branches/frame_order_testing/test_suite/unit_tests/_lib/_text/test_table.py Propchange: branches/frame_order_testing/ ------------------------------------------------------------------------------ --- svnmerge-integrated (original) +++ svnmerge-integrated Fri Mar 8 18:21:37 2013 @@ -1,1 +1,1 @@ -/trunk:1-18707 +/trunk:1-18713 Modified: branches/frame_order_testing/lib/text/table.py URL: http://svn.gna.org/viewcvs/relax/branches/frame_order_testing/lib/text/table.py?rev=18714&r1=18713&r2=18714&view=diff ============================================================================== --- branches/frame_order_testing/lib/text/table.py (original) +++ branches/frame_order_testing/lib/text/table.py Fri Mar 8 18:21:37 2013 @@ -28,6 +28,7 @@ # relax module imports. from check_types import is_float +from relax_errors import RelaxError # Special variables. @@ -66,6 +67,10 @@ for i in range(len(data)): # Loop over the columns. for j in range(len(data[i])): + # Skip multi-columns. + if data[i][j] == MULTI_COL: + continue + # Default left justification. justification[i][j] = 'l' @@ -95,6 +100,64 @@ # All other non-string types. elif not isinstance(data[i][j], str): data[i][j] = "%s" % data[i][j] + + +def _determine_widths(data=None, widths=None, separator=None): + """Determine the maximum column widths needed given the data. + + @keyword data: Either the headings or content converted to strings to check the widths of. + @type data: list of lists of str + @keyword widths: The list of widths to start with. If data is found to be wider than this list, then the width of that column will be expanded. + @type widths: list of int + @keyword separator: The column separation string. + @type separator: str + """ + + # The number of rows and columns. + num_rows = len(data) + num_cols = len(data[0]) + + # Determine the maximum column widths. + multi_col = False + for i in range(num_rows): + for j in range(num_cols): + # Switch the flag. + if data[i][j] == MULTI_COL: + multi_col = True + + # Skip multicolumn entries. + if data[i][j] == MULTI_COL or (j < num_cols-1 and data[i][j+1] == MULTI_COL): + continue + + # The element is larger than the previous. + if len(data[i][j]) > widths[j]: + widths[j] = len(data[i][j]) + + # Handle overfull multi-column cells. + if multi_col: + for i in range(num_rows): + for j in range(num_cols): + # End of multicolumn cell. + if data[i][j] == MULTI_COL and (j == num_cols-1 or (j < num_cols-1 and data[i][j+1] != MULTI_COL)): + col_sum_width = widths[j] + while 1: + # Walk back. + for k in range(j-1, -1, -1): + col_sum_width += len(separator) + widths[k] + + # Out of the cell. + if data[i][k] != MULTI_COL: + break + + # Nothing more to do. + break + + # The multicolumn width. + multi_col_width = len(data[i][k]) + + # The multicolumn cell is wider than the columns it spans, so expand the last column. + if multi_col_width > col_sum_width: + widths[j] += multi_col_width - col_sum_width def _rule(width=None, prefix=' ', postfix=' '): @@ -158,6 +221,8 @@ for j in range(i+1, num_col): if text[j] == MULTI_COL: width += len(separator) + widths[j] + else: + break # Add the padded text. if justification[i] == 'l': @@ -220,6 +285,18 @@ if headings != None: num_head_rows = len(headings) + # Column number checks. + if custom_format != None and len(custom_format) != num_cols: + raise RelaxError("The number of columns is %s but the number of elements in custom_format is %s." % (num_cols, len(custom_format))) + if headings != None: + for i in range(num_head_rows): + if len(headings[i]) != num_cols: + raise RelaxError("The %s columns does not match the %s elements in the heading row %s." % (num_cols, len(headings[i]), headings[i])) + for i in range(num_rows): + if len(contents[i]) != num_cols: + raise RelaxError("The %s columns does not match the %s elements in the contents row %s." % (num_cols, len(contents[i]), contents[i])) + + # Deepcopy so that modifications to the data are not seen. if headings != None: headings = deepcopy(headings) @@ -235,31 +312,13 @@ _convert_to_string(data=headings, justification=justification_headings, custom_format=custom_format) _convert_to_string(data=contents, justification=justification_contents, custom_format=custom_format) - # Initialise the pre-wrapping column widths. + # Determine the pre-wrapping column widths. prewrap_widths = [0] * num_cols - - # Determine the maximum column widths from the headers. - if headings != None: - for i in range(num_head_rows): - for j in range(num_cols): - # Skip multicolumn entries. - if headings[i][j] == MULTI_COL or (j < num_cols-1 and headings[i][j+1] == MULTI_COL): - continue - - # The element is larger than the previous. - if len(headings[i][j]) > prewrap_widths[j]: - prewrap_widths[j] = len(headings[i][j]) - - # Determine the maximum column widths from the content. - for i in range(num_rows): - for j in range(num_cols): - # Skip multicolumn entries. - if contents[i][j] == MULTI_COL or (j < num_cols-1 and contents[i][j+1] == MULTI_COL): - continue - - # The element is larger than the previous. - if len(contents[i][j]) > prewrap_widths[j]: - prewrap_widths[j] = len(contents[i][j]) + if headings != None: + data = headings + contents + else: + data = contents + _determine_widths(data=data, widths=prewrap_widths, separator=separator) # The free space for the text (subtracting the space used for the formatting). used = len(pad_left) Modified: branches/frame_order_testing/test_suite/unit_tests/_lib/_text/test_table.py URL: http://svn.gna.org/viewcvs/relax/branches/frame_order_testing/test_suite/unit_tests/_lib/_text/test_table.py?rev=18714&r1=18713&r2=18714&view=diff ============================================================================== --- branches/frame_order_testing/test_suite/unit_tests/_lib/_text/test_table.py (original) +++ branches/frame_order_testing/test_suite/unit_tests/_lib/_text/test_table.py Fri Mar 8 18:21:37 2013 @@ -250,3 +250,47 @@ self.assertEqual(len(true_table), len(table_lines)) for i in range(len(table_lines)): self.assertEqual(true_table[i], table_lines[i]) + + + def test_format_table6(self): + """Test 6 of the lib.text.table.format_table() function - no headings.""" + + # The table data. + headings = [['Model', 'k', 'chi2', 'AIC', 'Average position', MULTI_COL, MULTI_COL, 'Motional eigenframe', MULTI_COL, MULTI_COL, 'Order parameters (deg)', MULTI_COL, MULTI_COL], [None, None, None, None, 'a', 'b', 'g', 'a', 'b/th', 'g/ph', 'thx', 'thy', 'smax']] + contents = [['Rigid', 6, 1611.0583844357488, 1623.0583844357488, 2.7928187044509789, 6.241673451655573, 3.3350126302921255, None, None, None, None, None, None], ['Rotor', 9, 1393.0628812874404, 1411.0628812874404, 2.3720778521835015, 6.2511294411496241, 3.7347870727084764, None, 1.3782156252713658, 5.5998324326753401, None, None, 36.651271107544183], ['Iso cone, torsionless', 9, 1407.9014811061686, 1425.9014811061686, 2.2550248034078395, 6.2368882019396619, 3.891108977360032, None, 0.25090427716293384, 1.590485101074278, 21.287274572663485, None, None], ['Iso cone', 10, 1400.3558737738815, 1420.3558737738815, 2.8146957276396858, 6.2597080483925627, 3.2956149488567879, None, 1.3956123975976844, 5.5817149266639987, 10.300677006193942, None, 32.387495822632452], ['Pseudo ellipse, torsionless', 11, 1386.6214759007082, 1408.6214759007082, 2.6253119819082835, 6.2528446735668872, 3.4989380500907097, 2.692632830571366, 0.43833843941243616, 1.3038063115520346, 33.512494725673051, 15.888178532164503, None], ['Pseudo ellipse', 12, 1378.8893702060313, 1402.8893702060313, 2.7403158840045716, 6.259192518336242, 3.3759530521363121, 6.1651101516049849, 1.3600775439064279, 5.5851511636460813, 13.646328409458231, 0.74265383200964785, 31.027675419200627]] + + # Create the table. + table = format_table(headings=headings, contents=contents, custom_format=[None, None, "%.2f", "%.2f", "%.3f", "%.3f", "%.3f", "%.3f", "%.3f", "%.3f", "%.2f", "%.2f", "%.2f"]) + table_lines = table.split('\n') + + # The true table. + true_table = [ + " _______________________________________________________________________________________________________________________________ ", + " ", + " Model k chi2 AIC Average position Motional eigenframe Order parameters (deg) ", + " a b g a b/th g/ph thx thy smax ", + " _______________________________________________________________________________________________________________________________ ", + " ", + " Rigid 6 1611.06 1623.06 2.793 6.242 3.335 ", + " Rotor 9 1393.06 1411.06 2.372 6.251 3.735 1.378 5.600 36.65 ", + " Iso cone, torsionless 9 1407.90 1425.90 2.255 6.237 3.891 0.251 1.590 21.29 ", + " Iso cone 10 1400.36 1420.36 2.815 6.260 3.296 1.396 5.582 10.30 32.39 ", + " Pseudo ellipse, torsionless 11 1386.62 1408.62 2.625 6.253 3.499 2.693 0.438 1.304 33.51 15.89 ", + " Pseudo ellipse 12 1378.89 1402.89 2.740 6.259 3.376 6.165 1.360 5.585 13.65 0.74 31.03 ", + " _______________________________________________________________________________________________________________________________ ", + " ", + "" # This is because split combined with a final \n character. + ] + + # Printout. + print("The formatted table:") + for i in range(len(table_lines)): + print("'%s'" % table_lines[i]) + print("\nWhat the table should look like:") + for i in range(len(true_table)): + print("'%s'" % true_table[i]) + + # Check the table. + self.assertEqual(len(true_table), len(table_lines)) + for i in range(len(table_lines)): + self.assertEqual(true_table[i], table_lines[i])