1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
__author__ = 'magreene'
import collections
import re
from docutils import nodes
from docutils.parsers.rst import Directive
from docutils.statemachine import StringList
class progress(nodes.General, nodes.Element):
tagname = 'progress'
class ProgressTable(Directive):
has_content = True
required_arguments = 1
final_argument_whitespace = True
option_spec = {'text': str}
def create_headrow(self, label="Progress", classes=('prog-top-label',)):
hrow = nodes.row()
hrow += nodes.entry('', nodes.paragraph(text=label), classes=['head'] + list(classes))
hrow += nodes.entry('', nodes.paragraph(text='PLACEHOLDER'), classes=['PLACEHOLDER'])
return hrow
def create_progtable(self, **attrs):
_attrs = {
'classes': ['progress', 'outer', 'docutils', 'field-list'],
'colwidths': [20, 80]
}
_attrs.update(attrs)
# create container elements
node = nodes.table(classes=_attrs['classes'])
tgroup = nodes.tgroup(cols=2)
thead = thead = nodes.thead()
thead += self.create_headrow()
tbody = nodes.tbody()
# tgroup gets:
# - colspec
# - thead
# - tbody
for w in _attrs['colwidths']:
tgroup += nodes.colspec(colwidth=w)
# assemble the hierarchy
tgroup += thead
tgroup += tbody
node += tgroup
# return the table
return node
def add_progbar(self, row, val, max):
entry = nodes.entry(classes=['progcell', 'field-value'])
pbar = progress(value=val, max=max)
entry += pbar
row.replace(row.children[-1], entry)
def run(self):
secid = [self.arguments[0].lower().replace(' ', '-')]
section = nodes.section(ids=secid)
section.document = self.state.document
section += nodes.title(text=self.arguments[0])
# parse the 'text' option into the section, as a paragraph.
self.state.nested_parse(StringList([self.options['text']], parent=self), 0, section)
node = self.create_progtable()
section.children[-1] += node
head = node.children[0].children[-2].children[0]
body = node.children[0].children[-1]
comps = collections.OrderedDict()
cur = ""
for line in self.content:
# new list
nl = re.match(r'^:(?P<component>.+):$', line)
if nl is not None:
if cur != "":
# finish up shrow
self.add_progbar(shrow, len([c for c in comps[cur] if c is True]), len(comps[cur]))
cur = nl.groupdict()['component']
if cur not in comps:
comps[cur] = []
# shrow is the section header row
shrow = self.create_headrow(cur, classes=['field-name'])
body += shrow
continue
nl = re.match(r'^\s+- (?P<item>[^,]+),\s+(?P<value>(True|False))(, (?P<description>.+)$)?', line)
if nl is not None:
nl = nl.groupdict()
comps[cur].append(True if nl['value'] == "True" else False)
tr = nodes.row()
tr += nodes.description('',
nodes.inline(text="\u2713" if comps[cur][-1] else " "),
classes=['field-name', 'progress-checkbox'])
text_description = nodes.inline()
self.state.nested_parse(StringList(['{:s}'.format(nl['description'].lstrip() if nl['description'] is not None else ' ')], parent=self), 0, text_description)
tr += nodes.description('',
nodes.strong(text='{:s} '.format(nl['item'])),
text_description,
classes=['field-value'])
body += tr
if self.content:
# finish up the final hrow
self.add_progbar(shrow, len([c for c in comps[cur] if c == True]), len(comps[cur]))
# and fill in the end of mrow
self.add_progbar(head, len([c for r in comps.values() for c in r if c == True]), len([c for r in comps.values() for c in r]))
return [section]
def visit_progress(self, node):
attrs = {'value': 0,
'max': 0}
for a in attrs.keys():
if a in node.attributes:
attrs[a] = node.attributes[a]
self.body.append('<label>{}/{}</label>'.format(attrs['value'], attrs['max']))
self.body.append(self.starttag(node, node.tagname, **attrs))
self.body.append('</progress>')
def depart_progress(self, node):
pass
def setup(app):
app.add_css_file('progress.css')
app.add_node(progress, html=(visit_progress, depart_progress))
app.add_directive('progress', ProgressTable)
|