From 6d93be5276218c2a40393a577984bda35f163b0a Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Thu, 11 Oct 2018 15:31:24 -0500 Subject: [PATCH] Add member table generation This is taken from the OpenStack governance repo to generate the member table from a data file. Change-Id: I348d046603290fe6042b7d9070a6a71552742914 Signed-off-by: Dean Troyer --- doc/source/_exts/members.py | 156 ++++++++++++++++++++++++++++++++++++ doc/source/conf.py | 9 ++- reference/tsc/index.rst | 6 ++ reference/tsc/members | 5 ++ setup.cfg | 25 ++++++ setup.py | 22 +++++ tox.ini | 7 ++ 7 files changed, 226 insertions(+), 4 deletions(-) create mode 100644 doc/source/_exts/members.py create mode 100644 reference/tsc/members create mode 100644 setup.cfg create mode 100755 setup.py diff --git a/doc/source/_exts/members.py b/doc/source/_exts/members.py new file mode 100644 index 0000000..80e3d0e --- /dev/null +++ b/doc/source/_exts/members.py @@ -0,0 +1,156 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Build a table of the current members +""" + +import re + +from docutils import nodes +from docutils.parsers.rst.directives.tables import Table +from docutils.parsers.rst import directives + +# Full name (IRC) [expires in] {role} +_PATTERN = re.compile('(?P.*)\s+\((?P.*)\)\s+\<(?P.*)\>\s+\[(?P.*)\](\s+\{(?P.*)\})?') # noqa + + +def _parse_members_file(app, filename): + """Load the members file and return each row as a dictionary. + """ + with open(filename, 'r') as f: + for linum, line in enumerate(f, 1): + line = line.strip() + if not line or line.startswith('#'): + continue + m = _PATTERN.match(line) + if not m: + app.warning('Could not parse line %d of %s: %r' % + (linum, filename, line)) + continue + yield m.groupdict() + + +class MembersTable(Table): + """Insert the members table using the referenced file as source. + """ + + HEADERS = ('Full Name', 'IRC Nickname', 'E-mail', 'Term Expires', 'Role') + HEADER_MAP = { + 'Full Name': 'name', + 'IRC Nickname': 'irc', + 'E-mail': 'email', + 'Term Expires': 'date', + 'Role': 'role', + } + + option_spec = {'class': directives.class_option, + 'name': directives.unchanged, + 'datafile': directives.unchanged, + } + + has_content = False + + def run(self): + env = self.state.document.settings.env + app = env.app + config = app.config + + # The required argument to the directive is the name of the + # file to parse. + datafile = self.options.get('datafile') + if not datafile: + error = self.state_machine.reporter.error( + 'No filename in membertable directive', + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + return [error] + + # Handle the width settings and title + try: + # Since docutils 0.13, get_column_widths returns a (widths, + # colwidths) tuple, where widths is a string (i.e. 'auto'). + # See https://sourceforge.net/p/docutils/patches/120/. + col_widths = self.get_column_widths(len(self.HEADERS)) + title, messages = self.make_title() + except SystemMessagePropagation as detail: + return [detail.args[0]] + except Exception as err: + error = self.state_machine.reporter.error( + 'Error processing memberstable directive:\n%s' % err, + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno, + ) + return [error] + + # Now find the real path to the file, relative to where we are. + rel_filename, filename = env.relfn2path(datafile) + + # Build the table node using the parsed file + data_iter = _parse_members_file(app, filename) + table_node = self.build_table( + data_iter, + col_widths, + ) + table_node['classes'] += self.options.get('class', []) + self.add_name(table_node) + + if title: + table_node.insert(0, title) + + return [table_node] + messages + + def build_table(self, table_data, col_widths): + table = nodes.table() + + # Set up the column specifications + # based on the widths. + tgroup = nodes.tgroup(cols=len(self.HEADERS)) + table += tgroup + tgroup.extend(nodes.colspec(colwidth=col_width) + for col_width in col_widths) + + # Set the headers + thead = nodes.thead() + tgroup += thead + row_node = nodes.row() + thead += row_node + row_node.extend( + nodes.entry(h, nodes.paragraph(text=h)) + for h in self.HEADERS + ) + + # The body of the table is made up of rows. + # Each row contains a series of entries, + # and each entry contains a paragraph of text. + tbody = nodes.tbody() + tgroup += tbody + rows = [] + for row in table_data: + trow = nodes.row() + # Iterate over the headers in the same order every time. + for h in self.HEADERS: + # Get the cell value from the row data, replacing None + # in re match group with empty string. + cell = row.get(self.HEADER_MAP[h]) or '' + entry = nodes.entry() + para = nodes.paragraph(text=str(cell)) + entry += para + trow += entry + rows.append(trow) + tbody.extend(rows) + + return table + + +def setup(app): + app.info('loading members extension') + app.add_directive('memberstable', MembersTable) diff --git a/doc/source/conf.py b/doc/source/conf.py index fefddad..a4a53ec 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -14,9 +14,9 @@ # documentation root, use os.path.abspath to make it absolute, like shown # here. # -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) +import os +import sys +sys.path.insert(0, os.path.join(os.path.abspath('.'), '_exts')) # -- Project information ----------------------------------------------------- @@ -41,7 +41,8 @@ release = u'0.1' # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'openstackdocstheme' + 'members', + 'openstackdocstheme', ] # Add any paths that contain templates here, relative to this directory. diff --git a/reference/tsc/index.rst b/reference/tsc/index.rst index 1d8894b..c8b9d6d 100644 --- a/reference/tsc/index.rst +++ b/reference/tsc/index.rst @@ -17,6 +17,12 @@ documentation the links below are directly there. * `The Four Opens `__ * `StarlingX Charter `__ +Current Members +=============== + +.. memberstable:: + :datafile: ../../reference/tsc/members + Indices and Tables ================== diff --git a/reference/tsc/members b/reference/tsc/members new file mode 100644 index 0000000..d001c3b --- /dev/null +++ b/reference/tsc/members @@ -0,0 +1,5 @@ +# Full name (IRC) [expires in] {role} +Ian Jolliffe (ijolliffe) [TBD] +Brent Rowsell () [TBD] +Dean Troyer (dtroyer) [TBD] +Saul Wold (sdw) [TBD] diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..a9e1cbd --- /dev/null +++ b/setup.cfg @@ -0,0 +1,25 @@ +[metadata] +name = starlingx-governance +version = 2018.1 +summary = StarlingX Governance Documents +description-file = + README.rst +author = StarlingX TSC +author-email = starlingx-discuss@lists.starlingx.io +home-page = http://www.starlingx.io/ + +[files] +packages = + starlingx_governance + +[build_sphinx] +all_files = 1 +build-dir = doc/build +source-dir = doc/source +warning-is-error = 1 + +[pbr] +warnerrors = True + +[flake8] +ignore = E501,E226,H405 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..70c2b3f --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT +import setuptools + +setuptools.setup( + setup_requires=['pbr'], + pbr=True) diff --git a/tox.ini b/tox.ini index 1c6532f..ac6cefd 100644 --- a/tox.ini +++ b/tox.ini @@ -37,6 +37,13 @@ commands = -o -type f -name '*.yaml' \ -print0 | xargs -0 yamllint" +[testenv:pep8] +basepython = python3 +usedevelop = False +skip_install = True +deps = pep8 +commands = pep8 + [testenv:venv] basepython = python3 commands = {posargs}