================================= ``grail.reportlib`` Module ================================= .. include:: ..\version.h .. include:: ..\Library_Reference\lib-reference.h .. contents:: Table of Contents :backlinks: top -------- Overview -------- Creation of reports within the mining world is a common occurrence, with the creation of reports using Python, MineSightŪ Grail (MSGRAIL) and MineSightŪ Interactive Planner (MSIP) being no exception. Some goals of the reportlib are, 1. Provide a neutral means of storing tabular data that is independent of presentation. This also provides a means of organizing and stylizing the data prior to presentation. 2. Produce a useful Python module within our MSGRAIL library for generating reports in whatever MineSightŪ context the script writer may be in. Allowing the script writer to transfrom their tabular data into some common and useful presentations. 3. Provide a module that will start to store all the tools necessary for composing mining/exploration related reports. The typical workflow for the report lib module will be, 1. Create a :c:`Table` object. 2. Populate the :c:`Table` object with data. 3. Transform content of :c:`Table` object into a useful presentation. ------------------- Using the reportlib ------------------- As our first example will will build a memory resident table and present that table via a GView. This example will create a GView with a "Cut Name" and "Tonnes" header cells, and then iterate through all the cuts generating and filling in the tonnes for the two columns. .. Python:: from grail import reportlib # ... hRes = reserves.hGetCurResRef() dRes = reserves.dGetRes(hRes) reslib.ordergrades(dRes) table = reportlib.Table() table.write_row("Cut Name", "Tonnes", style="header") for cut in dRes['cuts']: name = cut['name'] tonnes = reslib.getcuttons(cut) tonnes_str = "%.0f" % (tonnes) table.write_row(name, tonnes_str) red, green, blue = 100, 0, 100 header_style = reportlib.Style(background=(red, green, blue)) style = {"header":header_style} handle = reportlib.transform.to_gview(table, "Cut Tonnage", "tonnage.grd", 1, style) gview.DisplayStdGrid(handle) The :f:`to_gview` function will read the :c:`Table` object and compose the appropriate GView. With a GView one is able to set background colors on a particular cell. The style dictionary used above illustrates what colors should be applied when a particular cell style is encountered within the :c:`Table` object during GView composition. The following example illustrates the ability to continuously write to the :c:`Table` object. With the :f:`write_row` function, one is able to simply send out rows of data, and be confident that the :c:`Table` object is capable of writing to the correct row number. .. Python:: from grail import reportlib table = reportlib.Table(default_style="standard") table.write_row("Column1", "Column2") table.write_row("Data1", "Data2") print table.current_write_row # 2 print table.previous_write_row # 1 print table.get(0, 0) # Column1 table.set_style(0, 0, "column1-header") table.set_style(0, 1, "column2-header") table.set_cell(2, 0, "Data3") print table.get_style(0, 0) # column1-header print table.num_rows # 3 print table.num_columns # 2 This generally allows you to stylize the table easily and efficiently while you are computing your report data. If we took the above table and applied the :f:`to_xhtml_table` transformation like this, .. Python:: xhtml_str = reportlib.transform.to_xhtml_table(table, hclass="mytable", summary="This is an Example table.") we would get the following output Extended HyperText Markup Language (XHTML) string [#xhtmlstandards]_, .. Python::
Column1 Column2
Data1 Data2
Data3
which you could then write to an XHMTL file and stylize according to your own Cascading Style Sheet (CSS) definition. Off course you can also use the :f:`to_xhtml` transformation function to compose a complete, simple document containing your :c:`Table` object's data (see `Generating a Complete XHTML Document`_). Generating a Complete XHTML Document ------------------------------------ In the `Using the reportlib`_ section we considered creating a Extendend HyperText Markup (XHTML) table based on the contents of our :c:`Table` object. In this section we will briefly go over how to create a complete XHMTL document using the grail.reportlib and the grail.misc.htmlgen_ modules. The steps for creating the document are, 1. Store data within a :c:`Table` object. 2. Transform the :c:`Table` object into a XHTML entity. 3. Embedded the
entity within a complete XHTML document. As a complete example consider the following, .. Python:: from grail import reportlib from grail.misc import htmlgen # Generate a simple table. table = reportlib.Table() table.write_row("Column 1", "Column 2", "Column 3") table.write_row("a", "b", "c") table.write_row("1", "2", "3") text = reportlib.tranform.to_xhtml_table(table) title = htmlgen.title("My First HTML Document") head = htmlgen.head(title) body = htmlgen.body(text) doc = htmlgen.HTMLDoc("myreport.html") doc.generate(head, body) Which generates a very simple document display of the data. To dress this document up a bit more consider adding a header via the :f:`htmlgen.h1` function. ----------- Table Class ----------- The :c:`Table` object and its functions and properties are discussed below. class :dc:`Table([default_value, default_style])` The :c:`Table` class provides one with the ability to store information in format that is *independent* of the actual presentation. This allows you to separate the composition of the report from the actual presentation of the report. Arguments: :a:`default_value` If a there is no value available for a given table cell, then this is what will be returned by :f:`get_value`. The default is value is a blank string (""). :a:`default_style` If there is no style available for a given table cell, then this is what will be returned by :f:`get_style`. The default style is the Python :d:`None`. Properties: :d:`num_columns` Equivalent to the largest column value written to the :c:`Table`. :d:`num_rows` Equivalent to the largest row value written to the :c:`Table`. :d:`current_write_row` This is the current writing row. In otherwords, this is where the next :f:`write_row` will be positioned. :d:`previous_write_row` Provided for convenience. This is the last row written to by :f:`write_row`. This is always equivalent to :d:`current_write_row` - 1. The :d:`previous_write_row` is convenient if you want to stylize some cell written in a previous script instruction. For example, .. Python:: table = Table() table.write_row("Column 1", "Column 2", "Column 3") table.set_style(table.previous_write_row, 0, "column1style") :df:`get_alias_value(alias)` Returns a value that is located at the cell with the given :a:`alias`. You can set an alias either via the :a:`alias` keyword in :f:`set_value` or via the :f:`set_alias` function. An example of using :f:`get_alias_value` would be, .. Python:: table = reportlib.Table() table.set_value(1, 1, met_volume, alias="Met Volume") # ... later ... value = table.get_alias_value("Met Volume") In this way you can essentially store and retrieve values within the table that are aliased with useful names. In effect, the :c:`Table` object becomes your data storage object during report generation. This function will generate a :e:`KeyError` if the given :a:`alias` has not been previously defined for this table. :df:`get_style(row, column)` Returns the style defined for the given row and column. If no style was defined, then the :a:`default_style` will be returned. :df:`get_value(row, column)` Returns the value defined for the given row and column. If no value was defined, then the :a:`default_value` will be returned. :df:`max_column_width(column)` Examines each row within the given column, attempts to convert the data into a string, and returns the length of the largest string within the column. :df:`minimize([when_value])` Walks through the table and will minimize the overall dimensions of the table. This will look at each cell, and determine if the cell's content is equivalent to :a:`when_value`. If the cell's contents are equivalent to :a:`when_value`, then the dimensions of the table will be reset. The default value for :a:`when_value` is the Python :a:`None`. If the default value is :a:`None`, then the value used throughout the minimization operation will be equivalent to the default value defined for the :a:`Table` object. In otherwords, if you do not specify a :a:`when_value` the minimize operation will attempt to do the right thing. This function can be best explained through an example, .. Python:: from grail import reportlib table = reportlib.Table(default_value=None) table.set_value(1, 1, "Contents") # at this point max row and max column are 1 and 1. table.set_value(10, 10, "Bigger") # at this point max row and max column are 10 and 10. # We decide to "delete" table.set_value(10, 10, None) # at this point max row and max column are still 10 and 10. table.minimize(when_value=None) # at this point max row and max column are now 1 and 1. The :f:`minimize` function will realize that everything outside of 1 by 1 table is equivalent to the :a:`when_value`, and minimize the table to the smallest dimensions possible. :df:`set_alias(row, column, alias)` Allows you to set an :a:`alias` for a given :a:`row` and :a:`column`. You can retrieve the value at the given :a:`alias` using the :f:`get_alias_value` function on the :c:`Table` object. :df:`set_column(start_row, column, *args[, style])` Accepts a start row, column index and argument a variable argument list of row contents. This will write the contents of the variable list argument into the given row starting at the given starting column. For example, .. Python:: table = Table() table.set_column(5, 1, "a", "b", "c") Is equivalent to writing out, .. Python:: table = Table() table.set_value(5, 1, "a") table.set_value(6, 1, "b") table.set_value(7, 1, "c") If your data is a Python list, you can use the "*" operator to pass in your list in lieu of a variable argument list. To extend the current example, you could also, .. Python:: data = ["a", "b", "c"] table = Table() table.set_column(5, 1, *data) and you will get the exactly the same results. The default style will be applied to each cell if no style is defined. :df:`set_row(row, start_column, *args[, style])` The same as :f:`set_column`, except this function will write out the contents of the variable argument list into the given row starting at the given start column. The default style will be applied to each cell if no style is defined. :df:`set_style(row, column, style)` Sets a style for the given row and column. :df:`set_value(row, column, value[, style, alias])` Sets a value for a given row and column. Optionally will set a style for the given row and column. The default style will be applied to the cell if no style is defined. The :a:`alias` allows you to give the :a:`row` and :a:`column` a useful name that can be referenced later via :f:`get_alias_value`. This allows you to effectively tag and reference values within the :c:`Table` object for later reference. :df:`write_row(*args[, style])` Utility function that allows you write variable argument lists in sequence to a :a:`Table` object. Each call to the :f:`write_row` will increment an internal pointer to the current row (:d:`current_write_row`). If you want to write a list of data, instead of arguments them selves you can use the "*" operator as in, .. Python:: data = ["1", "2", "3"] table = Table() table.write_row(*data) If no style is defined, the default style for the table will be appplied to each cell written to by the :f:`write_row` function. ----------- Style Class ----------- :dc:`Style([precision, width, justify, top_border, bottom_border, right_border, left_border, background, foreground])` The :c:`Style` class is used by the various transformations to help define how a cell is layed out. The :c:`Style` object is instaintiated with style attributes. These attributes are read during the transformation process. Each of the style attributes are as follows, Arguments: :a:`justify` Either :d:`reportlib.CENTER`, :d:`reportlib.RIGHT` or :d:`reportlib.LEFT`. See Constants_. The default is :d:`reportlib.RIGHT`. :a:`precision` : integer Indicates the number of decimal places to use for any floating point values found within a cell. The default is :d:`6`. :a:`width` : integer Indicates the width of a given cell. If the cells contents exceed the given width, the contents are truncated. The default is :d:`10`. :a:`top_border` : string A character to use when you wish to have a top border for a cell. The default is a blank string (:d:`""`). :a:`bottom_border` : string A character to use when you wish to have a bottom border for a cell. The default is a blank string (:d:`""`). :a:`right_border` : string A character to use when you wish to have a right border for a cell. The default is a blank string (:d:`""`). :a:`left_border` : string A character to use when you wish to have a left border for a cell. The default is a blank string (:d:`""`). :a:`background` : tuple A 3 value tuple specifying the RGB value of the background color. For example: (:a:`red`, :a:`green`, :a:`blue`). Black is :d:`(0, 0, 0)`, and white is :d:`(255, 255, 255)`. The default is white, :d:`(255, 255, 255)`. :a:`foreground` : tuple A 3 value tuple specifying the RGB value of the foreground color. For example: (:a:`red`, :a:`green`, :a:`blue`). Black is :d:`(0, 0, 0)`, and white is :d:`(255, 255, 255)`. The default is black, :d:`(0, 0, 0)`. Properties: :d:`precision` *Read only.* Number of decimal places to use for any floating point. :d:`width` *Read only.* Width in characters of a given cell. :d:`justify` *Read only*. Indicates the justification for a given cell. :d:`top_border` *Read only*. A character to use when you wish to have a top border for a cell. :d:`bottom_border` *Read only*. A character to use when you wish to have a bottom border for a cell. :d:`right_border` *Read only*. A character to use when you wish to have a right border for a cell. :d:`left_border` *Read only*. A character to use when you wish to have a left border for a cell. :d:`background` *Read only*. A 3 value tuple specifying the RGB value of the background color. For example: (:a:`red`, :a:`green`, :a:`blue`). Black is :d:`(0, 0, 0)`, and white is :d:`(255, 255, 255)`. :d:`foreground` *Read only*. A 3 value tuple specifying the RGB value of the foreground color. For example: (:a:`red`, :a:`green`, :a:`blue`). Black is :d:`(0, 0, 0)`, and white is :d:`(255, 255, 255)`. --------------- Transformations --------------- All transformations are accomplished through the :c:`Transfrom` singleton, which is accessible via :f:`reportlib.transform`. For example, consider the case of transforming your :c:`Table` object into a plain text representation. For example, .. Python:: from grail import reportlib table = reportlib.Table(default_value="0") table.set_value(0, 0, "1") table.set_value(1, 1, "1") table.set_value(2, 2, "1") # Transform to a TEXT representation via the "transform". text = reportlib.transform.to_text(table) The :f:`reportlib.transform.to_text` function reads the contents of the table and generates a simple text representation of that data. All available transformations are discussed below. :df:`to_csv(table[, replace_char])` Works like the :f:`to_text` transformation. This transformation will transform the contents of the :c:`Table` object into a comma delimited string. The string can either be displayed or written to a file. Each row will be terminated with a newline character. The :a:`replace_char` is used to replace any "," found in within a :c:`Table` object's cell. :df:`to_gview(table, title, filename, windownum[, style])` Will create a GView file with given window number and filename based on the contents of the :a:`table` and a :a:`style` dictionary. The :a:`style` dictionary defines how each cell is stylized via the :c:`Style` object (see `Style Class`_). If a style is found in the :c:`Table` object that is not defined in the style dictionary, the :d:`reportlib.DEFAULT_STYLE` will be applied. This function will return the handle to the *closed* window. You will have to execute :f:`gview.DisplayStdGrid` on the handle to display the window. See `Using the reportlib`_ for an example of using the :f:`to_gview` transformation. .. Warning:: The initial release of the this transformation in 3.40-00 defined a style as a 3 value tuple only. Althougth this will continue to be usable with the transformation, it is recommended that you use the :c:`Style` object with the :a:`background` attribute instead. :df:`to_text(table)` Will generate a standard text table based on the contents of the :c:`Table` object. The only formatting that will take place is an attempt to align columns, and space them by maximum width of the strings in each column. If you wish to have more control over your table layout consider using :f:`to_stylized_text`. Returns a string that can either be displayed or written to a file. Each row will be terminated with a newline character. :df:`to_stylized_text(table[, style])` Generates a text table that can take styles according to the :a:`style` argument. This table can generate a nicer layout than the basic :f:`to_text` transformation. Styles are specified via the :c:`Style` class and are stored within the :a:`style` dictionary object. As the transformation is processed, each cell within the :a:`table` is queried to determine its :c:`Style`. An example would be as follows, .. Python:: # Create our table. table = reportlib.Table(default_style="default") table.write_row("Column 1", "Column 2", "Column 3", style="header") table.write_row(1.2, 1.234, 1.2345) # Specify our styles. The header style will generate "=" around # each header. The default_style is display all cells with # precision 2, and place an empty space (" ") on the right border. header_style = reportlib.Style(justify=reportlib.CENTER, width=15, top_border="=", bottom_border="=", right_border=" ") default_style = reportlib.Style(precision=2, width=15, justify=reportlib.LEFT, right_border=" ") # Create our style dictionary. styles = {"header":header_style, # cells with "header" styles "default":default_style # all other styles } # Generate our output. text = reportlib.transform.to_stylized_text(table, styles) The above example will generate a table that appears like, :: =============== =============== =============== Column 1 Column 2 Column 3 =============== =============== =============== 1.20 1.23 1.23 You can specify other text formating options using the :c:`Style` objects as shown above. See `Style Class`_ for more information. .. Note:: When specifying the text :a:`width` via the :c:`Style` object be careful that you finally report is consistent down each column. :df:`to_xhtml_table(table[, hclass, summary])` This will read the :c:`Table` object and generate the appropriate Extended HyperText Markup Language (XHTML) table. The style will be applied to each cell as an XHTML class. The :a:`hclass` parameter will be used to define the class for the
entity itself. The summary parameter is used for the summary attribute on the
entity. The return value will be a string that can then be inserted into your own XHTML document. The
entity will be composed according the XHTML 1.0 standards [#xhtmlstandards]_, which rely on the HTML 4.01 standards for definitions of elements [#html401standards]_. Creating Custom Transformations ------------------------------- If the above transformations do not suite your needs, you can easily create your own transformations by traversing the :c:`Table` object yourself. As a useful example of creating your own transformation, consider creating a tab delimited file based on the contents of your :c:`Table` object. We will call our transformation function :f:`to_tab`. The function will be defined as, :: def to_tab(table) text = "" for row in range(table.num_rows): for col in range(table.num_columns): value = str(table.get_value(row, col)) value.replace("\t", " ") # avoid tabs in values. text += "%s" % (value) if col != table.num_columns-1: text += "\t" # avoid a tab at the very end. text += "\n" # new line at the end of each row. return text The "\\t" defines a Tab character, and the "\\n" defines a newline character. In our example we had to handle "\\t" within the :c:`Table` object's cell by replacing it with a blank string. To use our function we could create a table and write the tab delimited version out to a file. Something like this, .. Python:: from grail import reportlib table = reportlib.Table() table.write_row("Column 1", "Column 2", "Column 3") table.write_row("A", "B", "C") table.write_row("1", "2", "3") text = to_tab(table) f = open("mytabfile.tab", "w") f.write(text) f.close() Now our file system would have a "mytablfile.tab" written out with the contents of the table. --------- Constants --------- :dd:`CENTER`, :dd:`RIGHT`, :dd:`LEFT` Defines the type of justification to use with the :c:`Style` class. See `Style Class`_. :dd:`DEFAULT_STYLE` The default stylization used by the the transformations that require style definitions. ---- .. [#xhtmlstandards] "XHTML 1.0 The Extensible HyperText Markup Language (Second Edition)" W3C Recommendation. 1 August 2002. 8 July 2004. .. [#html401standards] Dave Ragget, et. al. "HTML 4.01 Specification". WC3 Recommendation. 24 December 1999. 8 July 2004. .. _grail.misc.htmlgen: grail.misc/lib-grail-misc-htmlgen.html