#!/usr/bin/python # -*- coding: utf-8 -*- # The MIT License (MIT) # # Copyright (c) 2014 Matheus Vieira Portela # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """simpletable.py - v0.1 2014-07-31 Matheus Vieira Portela This module provides simple classes and interfaces to generate simple HTML tables based on Python native types, such as lists. Author's website: http://matheusvportela.wordpress.com/ v0.4 2019-05-24 by Walter Schwenger """ ### CHANGES ### # 2014-07-31: v0.1 MVP: # - First version # 2014-08-05: v0.2 MVP: # - Method for defining header rows # - SimpleTable method to create a SimpleTable from lists # - Method to create a table from a simple list of elements and a column size # 2014-08-20: v0.3 MVP: # - Enable SimplePage to accept a list of tables # - Enable SimplePage to iterate over its tables # 2019-05-24: v0.4 WS: # - Added SimpleTableImage class to handle adding images to tables # - Added test images and image example to __main__ ### REFERENCES ### # Decalage HTML.py module: http://www.decalage.info/python/html import codecs # noinspection PyCompatibility,PyUnresolvedReferences def quote(string): try: from urllib.parse import quote return quote(string) except ModuleNotFoundError: from urllib import pathname2url return pathname2url(string) class SimpleTableCell(object): """A table class to create table cells. Example: cell = SimpleTableCell('Hello, world!') """ def __init__(self, text, header=False): """Table cell constructor. Keyword arguments: text -- text to be displayed header -- flag to indicate this cell is a header cell. """ self.text = text self.header = header def __str__(self): """Return the HTML code for the table cell.""" if self.header: return '%s' % (self.text) else: return '%s' % (self.text) class SimpleTableImage(object): """A table class to create table cells with an image. Example: cell = SimpleTableImage('images/image_1.jpg') """ def __init__(self, image_file, width=None, height=None): """Table cell constructor. Keyword arguments: image_file -- relative filepath to image file to display. width -- (optional) width of the image in pixels height -- (optional) height of the image in pixels """ self.image_file = image_file if width: self.width = round(width) else: self.width = width if height: self.height = round(height) else: self.height = height def __str__(self): """Return the HTML code for the table cell with the image.""" safe_filename = quote(self.image_file) output = '' % (safe_filename) output += '') for cell in self.cells: row.append(str(cell)) row.append('') return '\n'.join(row) def __iter__(self): """Iterate through row cells""" for cell in self.cells: yield cell def add_cell(self, cell): """Add a SimpleTableCell object to the list of cells.""" self.cells.append(cell) def add_cells(self, cells): """Add a list of SimpleTableCell objects to the list of cells.""" for cell in cells: self.cells.append(cell) class SimpleTable(object): """A table class to create HTML tables, populated by HTML table rows. Example: # Table from lists table = SimpleTable([['Hello,', 'world!'], ['How', 'are', 'you?']]) # Table with header row table = SimpleTable([['Hello,', 'world!'], ['How', 'are', 'you?']], header_row=['Header1', 'Header2', 'Header3']) # Table from SimpleTableRow rows = SimpleTableRow(['Hello,', 'world!']) table = SimpleTable(rows) """ def __init__(self, rows=None, header_row=None, css_class=None): """Table constructor. Keyword arguments: rows -- iterable of SimpleTableRow header_row -- row that will be displayed at the beginning of the table. if this row is SimpleTableRow, it is the programmer's responsibility to verify whether it was created with the header flag set to True. css_class -- table CSS class """ rows = rows or [] if isinstance(rows[0], SimpleTableRow): self.rows = rows else: self.rows = [SimpleTableRow(row) for row in rows] if header_row is None: self.header_row = None elif isinstance(header_row, SimpleTableRow): self.header_row = header_row else: self.header_row = SimpleTableRow(header_row, header=True) self.css_class = css_class def __str__(self): """Return the HTML code for the table as a string.""" table = [] if self.css_class: table.append('' % self.css_class) else: table.append('
') if self.header_row: table.append(str(self.header_row)) for row in self.rows: table.append(str(row)) table.append('
') return '\n'.join(table) def __iter__(self): """Iterate through table rows""" for row in self.rows: yield row def add_row(self, row): """Add a SimpleTableRow object to the list of rows.""" self.rows.append(row) def add_rows(self, rows): """Add a list of SimpleTableRow objects to the list of rows.""" for row in rows: self.rows.append(row) class HTMLPage(object): """A class to create HTML pages containing CSS and tables.""" def __init__(self, tables=None, css=None, encoding="utf-8"): """HTML page constructor. Keyword arguments: tables -- List of SimpleTable objects css -- Cascading Style Sheet specification that is appended before the table string encoding -- Characters encoding. Default: UTF-8 """ self.tables = tables or [] self.css = css self.encoding = encoding def __str__(self): """Return the HTML page as a string.""" page = [] if self.css: page.append('' % self.css) # Set encoding page.append('' % self.encoding) for table in self.tables: page.append(str(table)) page.append('
') return '\n'.join(page) def __iter__(self): """Iterate through tables""" for table in self.tables: yield table def save(self, filename): """Save HTML page to a file using the proper encoding""" with codecs.open(filename, 'w', self.encoding) as outfile: for line in str(self): outfile.write(line) def add_table(self, table): """Add a SimpleTable to the page list of tables""" self.tables.append(table) def fit_data_to_columns(data, num_cols): """Format data into the configured number of columns in a proper format to generate a SimpleTable. Example: test_data = [str(x) for x in range(20)] fitted_data = fit_data_to_columns(test_data, 5) table = SimpleTable(fitted_data) """ num_iterations = len(data) / num_cols if len(data) % num_cols != 0: num_iterations += 1 return [data[num_cols * i:num_cols * i + num_cols] for i in range(num_iterations)]