Source code for summarynb

import os
import pandas as pd
from IPython.display import HTML, display

"""Top-level package for Summary Notebooks."""

__author__ = """Maxim Zaslavsky"""
__email__ = "maxim@maximz.com"
__version__ = "0.1.4"

"""Functions that return functions that return HTML."""

# TODO: make max_width a property of <td>, not <img>?


[docs]def image(img_src): """Renders an image. :param img_src: Image filename. :type img_src: str :return: Template function that accepts a max pixel width integer and returns HTML. :rtype: function """ # Convert absolute path to relative path, since a browser won't be able to grab an image from "/home/..." img_src = os.path.relpath(img_src) def template(max_width, max_height, *args, **kwargs): def convert_to_px_or_unset(optional_numeric_value): if not optional_numeric_value: return "inherit" return str(optional_numeric_value) + "px" max_width = convert_to_px_or_unset(max_width) max_height = convert_to_px_or_unset(max_height) return """<img src="{img_src}" style="max-width: {max_width}; max-height: {max_height};" />""".format( img_src=img_src, max_width=max_width, max_height=max_height ) return template
[docs]def table(df): """Renders a Pandas dataframe. :param df: the dataframe :type df: pandas.DataFrame :return: Template function that returns HTML. :rtype: function """ def template(*args, **kwargs): return df.to_html() return template
[docs]def plaintext(text, **kwargs): """Renders plain text. :param text: text to display :type text: str """ def template(*args, **kwargs): # convert to html return f"<pre>{text}</pre>" return template
[docs]def empty(width=None): """Creates empty separator block. :param width: specify block width, defaults to None :type width: int, optional """ def template(max_width, *args, **kwargs): # empty cell return f'<div style="min-width: {max_width if not width else width}px;"></div>' return template
"""Extensions that simplify usage of the above base functions."""
[docs]def csv(fname, cols=None, **kwargs): """ Read a csv from [fname]. Optionally subset to columns [cols]. Kwargs passed onto pandas read_csv. """ df = pd.read_csv(fname, **kwargs) if cols is not None: df = df[cols] return table(df)
[docs]def indexed_csv(fname, cols=None, **kwargs): """ Read a csv from [fname], and set first column as index column. Optionally subset to columns [cols]. Kwargs passed onto pandas read_csv. Useful for reading in pandas series quickly. """ return csv(fname, cols=cols, index_col=0, **kwargs)
[docs]def textfile(fname, **kwargs): """ Read a text file and render as plain text. """ with open(fname, "r") as f: text = f.read() return plaintext(text)
"""Main logic.""" def _get_template(user_input): """Return executable template function if not provided, based on filename.""" if callable(user_input): # this is already a template function return user_input # user provided a filename # get extension extension = os.path.splitext(user_input)[1] # detect different table types if extension == ".csv": return csv(user_input) elif extension == ".tsv": return csv(user_input, sep="\t") elif extension == ".txt": return textfile(user_input) # assume it's an image return image(user_input) def _ensure_list_of_lists(entries): """Transform input to being a list of lists.""" if not isinstance(entries, list): # user passed in single object # wrap in a list # (next transformation will make this a list of lists) entries = [entries] if not any(isinstance(element, list) for element in entries): # user passed in a list of objects, not a list of lists # wrap in a list entries = [entries] return entries def _flatten(lst): """Flatten sublists in list. see https://stackoverflow.com/a/952952/130164 """ from collections.abc import Sequence entries = [] for sublist in lst: # may be a sublist if isinstance(sublist, Sequence) and not isinstance( sublist, (str, bytes, bytearray) ): # recursively flatten sublist entries.extend(_flatten(sublist)) else: # just an item entries.append(sublist) return entries
[docs]def chunks(entries, shape): """Reshape [entries] into chunks of shape [shape], which can be: - a (number of rows, number of columns) tuple, in which case we verify length - or simply a number of columns, in which case we guess the right number of rows, and allow in-complete rows.""" # Flatten entries entries = _flatten(entries) if isinstance(shape, list) or isinstance(shape, tuple): assert len(shape) <= 2, "Only supports 2D arrays" if len(shape) == 2: # Confirm length assert len(entries) == shape[0] * shape[1], "Wrong length." # Convert shape to (n_rows, n_cols) tuple. if not isinstance(shape, list) and not isinstance(shape, tuple): # Input was a single object. shape = (0, shape) if len(shape) == 1: # Input was a list of tuple of length 1. shape = (0, shape[0]) n_col = shape[1] reshaped = [ entries[row_num * n_col : (row_num + 1) * n_col] for row_num in range(len(entries) // n_col) ] if len(entries) % n_col > 0: # handle remainder with an uneven row reshaped.append(entries[(len(entries) // n_col) * n_col :]) return reshaped
def _make_HTML(entries, headers, max_width, max_height): """ Create HTML table. """ def wrap_in_column(contents): return """<td style="text-align: center">{contents}</td>""".format( contents=contents ) def wrap_in_row(contents): return """<tr>{contents}</tr>""".format(contents=contents) def make_headers(headers): if headers is not None: return """<tr>{contents}</tr>""".format( contents="\n".join( [ """<th style="text-align: center">%s</th>""" % str(header).strip() for header in headers ] ) ) return "" def wrap_in_table(contents): return """<table>{contents}</table>""".format(contents=contents) # Transform to list of lists (list of rows that are each a list of columns), if user passed in single object (1 row, 1 column) or a single list (1 row, many columns) entries = _ensure_list_of_lists(entries) # Make HTML for each row rows = [ "\n".join( [ wrap_in_column(_get_template(template)(max_width, max_height)) for template in row ] ) for row in entries ] # assemble rows into table return wrap_in_table( make_headers(headers) + "\n".join([wrap_in_row(row) for row in rows]) )
[docs]def show(entries, headers=None, max_width=800, max_height=800): """ Display chosen figures and tables in an HTML table. Parameters: - [entries]: List of rows. Each row is a list of columns. Each column is a list of executable functions that return an HTML template. For convenience: - Raw filenames (not wrapped in executable functions that return HTML templates) are also accepted, and will be auto-wrapped based on file extension. - If a list of lists is not provided, the input is interpreted as forming columns in a single row. - [headers]: List of column headers. - [max_width]: set max pixel width for images, default 800px. set to None to disable max width. - [max_height]: set max pixel height for images, default 800px. set to None to disable max height. """ return display( HTML( _make_HTML( entries, headers=headers, max_width=max_width, max_height=max_height ) ) )