Source code for uw.web.html.format

"""Miscellaneous HTML formatting routines.

Routines for formatting various objects as HTML, a convenience
routine for slightly simplifying the provision of manually-generated
"breadcrumb" links, and a routine for formatting simple HTML tables.
"""

import datetime
from urllib.parse import quote, urlencode, urlunsplit

from ll.xist.ns import html

from uw.web.html.join import html_join

[docs]def format (item): """Format an item as HTML. :param item: a value to format as HTML in a reasonable default way. If the input is None, the output is None. If the input is a datetime type, the .isoformat() method is used. Otherwise, the item is converted to a string using str(). """ if item is None: return None if isinstance (item, datetime.date) or isinstance (item, datetime.time) \ or isinstance (item, datetime.datetime): return item.isoformat () return str (item)
[docs]def format_date (date, none=None): """Format a date in the standard ISO 8601 form. :param date: The date to format (datetime.date) or None. :param none: The value to use if the input is None. Formats the date in the standard ISO 8601 format. If the input is None or the maximum date or datetime, the output is the specified alternate value or None. """ if date in [None, datetime.datetime.max, datetime.date.max]: return none return date.strftime ('%Y-%m-%d')
[docs]def format_time (time, none=None): """Format a time in the standard ISO 8601 form. :param time: The date to format (datetime.time) or None. :param none: The value to use if the input is None. Formats the time in the standard ISO 8601 format. If the input is None, the output is the specified alternate value or None. """ if time in [None]: return none return time.strftime ('%H:%M')
[docs]def format_datetime (time, none=None): """Format a datetime in the standard ISO 8601 form. :param time: The datetime to format (datetime.datetime) or None. :param none: The value to use if the input is None. Formats the datetime in the standard ISO 8601 format, to the minute. If the input is None or the maximum datetime, the output is the specified alternate value or None. """ if time in [None, datetime.datetime.max]: return none return time.strftime ('%Y-%m-%d %H:%M')
[docs]def format_email (email, subject=None): """Format an email address as HTML. :param email: an email address. Returns a mailto link for the specified email address. """ if email is None: return None query = {} if subject is not None: query['subject'] = subject url = urlunsplit (('mailto', None, email, urlencode (query, quote_via=quote), None)) return html.a (html.tt (email), href=url)
[docs]def format_expandable_div (lst): """Format a list of expandable div name and expandable div content pairs as html. :param lst: List of (Name, Content) of the expandable divs. :return: Formatted HTML for WCMS expandable divs from a list of div title and div content tuples. """ expandable_div = html.div ({"class":"field-item even", "property":"content:encoded"}) for name, content in lst: expandable = html.div (class_="expandable") expandable.append (html.h2 (html.div (name, {"aria-expanded":"false"}))) expandable.append (html.div (content, class_="expandable-content")) expandable_div.append (expandable) return expandable_div
[docs]def format_return (*names, **keys): """Return HTML formatting of "breadcrumbs". :param \*names: the short titles to use for href="..", href="../..", etc. :param keys['dot']: the short title to use for href=".". Returns an HTML <p> with relative links to parent directories corresponding to the parameter names provided, and possibly an additional link to the current directory if a "dot" keyword argument is provided. The link text is the provided names. If a provided name is None, the corresponding link is skipped. This is a way of making it easy to jump back from a detailed page to a higher level of the application. Unfortunately higher levels of the navigation tend to get repeated everywhere. **TODO: implement real breadcrumbs integrated with a menu system and with page titles.** """ length = len (names) result = [None] * length for i, name in enumerate (names): if name is not None: result[i] = html.a (name, href='/'.join ([".."] * (length - i))) if 'dot' in keys: result.append (html.a (keys['dot'], href=".")) return html.p ('Return to ', html_join (result, ', '))
[docs]def format_tabs (lst, active_id=None): """Format a list of tab name, tab id, and tab content triples as HTML. :param lst: List of (tab name, tab id, tab content). :param active_id: id of the default tab. :return: Formatted HTML for WCMS tabs that is recognizable by the WCMS CSS and Javascript. """ result = [html.div (class_="uw-site-homepage-tabs")] for name, tab_id, content in lst: result.append (html.div ( html.h2 (name, { "class":"tab-link %s" % ("block_current" if tab_id == active_id else ''), "data-block-tab":"%s-tab" % tab_id }), html.div (content, id="%s-tab" % tab_id, class_="item-class %s" % ("block_current" if tab_id == active_id else '')), id="block-uw-ct-%s-tab-item-front-page" % tab_id, class_="block-list" )) return result
[docs]def make_table_format (*columns, **keys): """Compute a function for producing an HTML <table>. :param columns: a list of column definitions. :param keys['header_row']: a function to be invoked before the first item row which may also return an extra row. :param keys['before_row']: a function to be invoked before each item row which may also return an extra row. :param keys['after_row']: a function to be invoked after each item row which may also return an extra row. :param keys['footer_row']: a function to be invoked after the last item row which may also return an extra row. :param keys['row_attributor']: a function to be invoked on each item which returns a dictionary of attributes for the <tr> element. :param keys['use_colgroup']: whether or not to include an HTML <colgroup> element at the beginning of the table. Returns a function which, when passed a list of items, returns an HTML <table> with one column for each entry in columns, and one row for each item in the list of items, as well as a header row and possibly some additional rows depending on the optional keyword parameters supplied. Each element of columns is a column definition, which is a 4-tuple consisting of: - an HTML <col> element for the column; - the HTML contents of the header for the column; - a function which takes an item and returns HTML for the column; - a function which takes an item and returns HTML attributes for the <td> element. The later elements of the tuple may be omitted in which case appropriate neutral values will be used. The <col> element may also be omitted; this case is recognized when the first element of the tuple is not an ll.xist.ns.html.col object. The \*_row keyword parameters specify additional computations to take place at the noted point in computation of the result table. They may also return additional rows if that is useful in a particular use. One use of this is for header_row to initialize counters or totals, and footer_row to produce a row which displays the final results. """ colgroup = [] headings = [] formatters = [] for column in columns: column = list (column) if len (column) < 1 or type (column[0]) is not html.col: column.insert (0, html.col ()) if len (column) < 2: column.append (None) if len (column) < 3: column.append (lambda r: None) if len (column) < 4: column.append (lambda r: {}) col, heading, formatter, attributor = column colgroup.append (col) headings.append (heading) formatters.append ((formatter, attributor)) headings = tuple (headings) formatters = tuple (formatters) header_row = keys.get ('header_row') before_row = keys.get ('before_row') after_row = keys.get ('after_row') footer_row = keys.get ('footer_row') row_attributor = keys.get ('row_attributor', lambda r: {}) use_colgroup = keys.get ('use_colgroup') def format_table (rows): result = html.table ( html.colgroup (col for col in colgroup) if use_colgroup else None, html.tr (html.th (heading) for heading in headings), ) if header_row is not None: result.append (header_row ()) for row in rows: if before_row is not None: result.append (before_row (row)) result.append (html.tr ( [html.td (formatter (row), **attributor (row)) for formatter, attributor in formatters], **row_attributor (row) )) if after_row is not None: result.append (after_row (row)) if footer_row is not None: result.append (footer_row ()) return result return format_table