summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/gen-quip-0000.py73
-rwxr-xr-xscripts/quip2html.py265
-rw-r--r--scripts/setup.mk21
-rw-r--r--scripts/template.diff73
4 files changed, 432 insertions, 0 deletions
diff --git a/scripts/gen-quip-0000.py b/scripts/gen-quip-0000.py
new file mode 100755
index 0000000..0f2ded9
--- /dev/null
+++ b/scripts/gen-quip-0000.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2017 The Qt Company Ltd.
+# Contact: http://www.qt.io/licensing/
+#
+# You may use this file under the terms of the CC0 license.
+# See the file LICENSE.CC0 from this package for details.
+
+from email.parser import HeaderParser
+from glob import glob
+import os.path
+
+class RootQuip (dict):
+ def __init__(self, files):
+ parser = HeaderParser()
+ for quip in files:
+ with open(quip, 'r') as quipfp:
+ self[quip] = parser.parse(quipfp)
+
+ def last_name(names): # tool function for owners; not a method
+ while names and names[-1].startswith('<') and names[-1].endswith('>'):
+ names.pop()
+ return names[-1]
+
+ @staticmethod
+ def owners(authors, final=last_name):
+ return ', '.join(final(s) for s in (a.split() for a in authors.split(',')))
+ del last_name
+
+ def quip_entries(self):
+ fmt = ' <tr><td>%4s</td><td><a href="quip-%04d.html">%04d</a></td><td>%s</td><td>%s</td></tr>'
+ for filename in sorted(self):
+ q = self[filename]
+ num = int(q['QUIP'])
+ yield fmt % (q['Type'][:4], num, num, q['Title'], self.owners(q['Author']))
+
+ preamble = """\
+QUIP: 0
+Title: QUIP Index
+Author: Louai Al-Khanji, Edward Welbourne
+Status: Active
+Type: Informational
+Created: 2016-09-16
+Post-History: 2017-03-15
+
+QUIP Index
+==========
+
+This QUIP contains the index of all finalized Qt User-Submitted
+Improvement Proposals, known as QUIPs. A list of reviews of quip
+module changes, including any QUIPs under development, can be found in
+the code-review listing [0]_.
+
+The following QUIPs exist:
+
+.. raw:: html
+
+ <table>
+ <tr><th>type</th><th>number</th><th>title</th><th>owners</th></tr>
+"""
+
+ endpiece = """\
+ </table>
+
+.. [0] https://codereview.qt-project.org/#/q/project:meta/quips,n,z
+"""
+
+ def compose_body(self):
+ return self.preamble + '\n'.join(self.quip_entries()) + self.endpiece
+
+if __name__ == '__main__':
+ import sys
+ print RootQuip(sys.argv[1:]).compose_body()
diff --git a/scripts/quip2html.py b/scripts/quip2html.py
new file mode 100755
index 0000000..544b472
--- /dev/null
+++ b/scripts/quip2html.py
@@ -0,0 +1,265 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2017 The Qt Company Ltd.
+# Contact: http://www.qt.io/licensing/
+#
+# You may use this file under the terms of the CC0 license.
+# See the file LICENSE.CC0 from this package for details.
+"""Qt's Utilitarian Improvement Process (QUIP) processing
+
+Adapted from the python project's similar code for PEPs.
+"""
+
+from __future__ import absolute_import, with_statement
+from __future__ import print_function
+
+__docformat__ = 'reStructuredText'
+
+import os, sys, subprocess
+
+from docutils import core, nodes, utils, languages, DataError
+from docutils.readers import standalone
+from docutils.transforms import Transform, frontmatter, parts
+from docutils.parsers import rst
+from docutils.writers import html4css1
+
+class QuipHeaders(Transform):
+ """Refines the table displaying RFC 2822 headers
+
+ Checks all mandatory headers are present, inserts Version and
+ Last-Modified (in that order, after Title).
+
+ FIXME: Version should probably be called Commit, as it's a
+ commit-id, not any sort of sequential version number, which is
+ what the name "Version" is apt to conjure up in the reader's mind.
+ """
+ default_priority = 360
+
+ @staticmethod
+ def raw_node(text):
+ return nodes.raw('', text, format='html')
+
+ @staticmethod
+ def field_name(text):
+ text = '-'.join(x.capitalize() for x in text.split('-'))
+ return nodes.field_name('', text, format='html')
+
+ @staticmethod
+ def field_body(text):
+ return nodes.field_body('', nodes.paragraph('', text), format='html')
+
+ @classmethod
+ def mask_email(cls, ref):
+ """
+ Mask the email address in `ref` and return a replacement node.
+
+ `ref` is returned unchanged if it contains no email address.
+
+ For email addresses such as 'user@host', mask the address as 'user
+ at host' (text) to thwart simple email address harvesters.
+ """
+ if ref.hasattr('refuri') and ref['refuri'].startswith('mailto:'):
+ return cls.raw_node(ref.astext().replace('@', '&#32;&#97;t&#32;'))
+ return ref
+
+ # Populated by main():
+ git_data = {}
+
+ def apply(self):
+ # QUIP 1 says these should be in this order - not checked.
+ required = ['quip', 'title', 'author', 'status', 'type', 'created',
+ 'post-history',
+ ]
+ insertor = None # index after which to insert git_data entries
+
+ for index, field in enumerate(self.document[0]):
+ name = field[0].astext().lower()
+ body = field[1]
+ if name in required:
+ required.remove(name)
+ # else: optional fields - could check whether recognized
+ if name == 'title' and insertor is None:
+ insertor = index
+
+ if len(body) > 1:
+ raise DataError(
+ 'QUIP header field body contains several elements:\n%s'
+ % field.pformat(level=1))
+ elif name in self.git_data:
+ sys.stderr.write('Over-writing %s header content: %s\n'
+ % (field[0].astext(), body.astext()))
+ body.replace_self(self.field_body(self.git_data[name]))
+ del self.git_data[name]
+ if name == 'version': # appears before last-modified
+ insertor = index
+ elif len(body): # NB: body is a Node, true even if empty
+ para, = body
+ if not isinstance(para, nodes.paragraph):
+ raise DataError(
+ 'QUIP header field body may only contain a single paragraph:\n%s'
+ % field.pformat(level=1))
+ elif name == 'quip':
+ try:
+ int(body.astext()) # evaluate to check it *is* an int ...
+ except (ValueError, TypeError):
+ raise DataError(
+ 'QUIP header QUIP has non-numeric value: ' + body.astext())
+ elif name == 'author':
+ for node in para:
+ if isinstance(node, nodes.reference):
+ node.replace_self(self.mask_email(node))
+
+ # Check for missing mandatory field
+ if required:
+ sys.stderr.write('Missing mandatory header(s): ' + ', '.join(required) + '\n')
+ # raise DataError() instead, once post-history is fixed in all QUIPs
+
+ # Insert any git_data fields not replaced on the way:
+ for key in reversed(sorted(self.git_data)):
+ value = self.git_data[key]
+ field = self.document[0][insertor].deepcopy()
+ insertor += 1
+ field[0].replace_self(self.field_name(key))
+ field[1].replace_self(self.field_body(value))
+ self.document[0].insert(insertor, field)
+
+class QuipContents(Transform):
+
+ """
+ Insert an empty table of contents topic and a transform placeholder into
+ the document after the RFC 2822 header.
+ """
+
+ default_priority = 380
+
+ def apply(self):
+ language = languages.get_language(self.document.settings.language_code,
+ self.document.reporter)
+ name = language.labels['contents']
+ title = nodes.title('', name)
+ topic = nodes.topic('', title, classes=['contents'])
+ name = nodes.fully_normalize_name(name)
+ if not self.document.has_name(name):
+ topic['names'].append(name)
+ self.document.note_implicit_target(topic)
+ pending = nodes.pending(parts.Contents)
+ topic += pending
+ self.document.insert(1, topic)
+ self.document.note_pending(pending)
+
+class QuipReader(standalone.Reader):
+ # Based on PEP reader: docutils.readers.pep
+ inliner_class = rst.states.Inliner
+
+ def get_transforms(self):
+ transforms = standalone.Reader.get_transforms(self)
+ # We have QUIP-specific frontmatter handling.
+ transforms.remove(frontmatter.DocTitle)
+ transforms.remove(frontmatter.SectionSubTitle)
+ transforms.remove(frontmatter.DocInfo)
+ transforms.extend([QuipHeaders, QuipContents])
+ return transforms
+
+ def __init__(self, parser=None, parser_name=None):
+ """`parser` should be ``None``."""
+ if parser is None:
+ parser = rst.Parser(rfc2822=True, inliner=self.inliner_class())
+ standalone.Reader.__init__(self, parser, '')
+
+class QuipWriter(html4css1.Writer):
+ # Assumed to be run from the build directory
+ cwd_file = os.path.join(os.getcwd(), 'dummy')
+ script_dir = os.path.dirname(__file__)
+
+ default_template = 'template.html'
+ default_stylesheet = utils.relative_path(
+ cwd_file, os.path.join(script_dir, '../docutils/quip.css'))
+ # Can we arrange to just use /usr/share/docutils/writers/pep_html/pep.css instead ?
+ del cwd_file, script_dir
+
+ # Used by base-class somehow ?
+ settings_default_overrides = {
+ 'stylesheet_path': default_stylesheet, # => subs['stylesheet'], below
+ 'template': default_template,
+ }
+ del default_template, default_stylesheet
+
+ relative_path_settings = ('template',)
+
+ def interpolation_dict(self):
+ """Set up the substitions in our template
+
+ The template can contain tokens of form %(name)s which get
+ expanded to subs[name] expressed as a string, for various
+ names. The base-class's version of this provides 'stylesheet'
+ as one key of the resulting dictionary, populated from the
+ settings_default_overrides['stylesheet_path'] above.
+ """
+ subs = html4css1.Writer.interpolation_dict(self)
+ settings = self.document.settings
+ # Presently unused:
+ # subs['qthome'] = 'http://www.qt.io'
+ # subs['quiphome'] = 'http://quips.qt.io'
+ # subs['quipindex'] = 'http://quips.qt.io'
+ index = self.document.first_child_matching_class(nodes.field_list)
+ header = self.document[index]
+ self.quipnum = header[0][1].astext()
+ subs['quip'] = self.quipnum
+ try:
+ subs['quipnum'] = '%04i' % int(self.quipnum)
+ except ValueError:
+ subs['quipnum'] = self.quipnum
+ self.title = header[1][1].astext()
+ subs['title'] = 'QUIP %s | %s' % (self.quipnum, self.title)
+ subs['body'] = ''.join(self.body_pre_docinfo + self.docinfo + self.body)
+ return subs
+
+ def assemble_parts(self):
+ html4css1.Writer.assemble_parts(self)
+ self.parts['title'] = [self.title]
+ self.parts['quipnum'] = self.quipnum
+
+class GitRunner(object):
+ def __init__(self, filename, stderr):
+ self.path, self.base = os.path.split(filename)
+ self.stderr = stderr
+
+ def run(self, args):
+ proc = subprocess.Popen(['git'] + args,
+ stderr=self.stderr, stdout=subprocess.PIPE,
+ cwd=self.path, universal_newlines=True)
+ line = proc.stdout.readline()
+ proc.wait()
+ return line
+
+def main(filename, source, output, stderr):
+ git = GitRunner(filename, stderr)
+ data = QuipHeaders.git_data
+ data['version'] = git.run(['rev-list', 'HEAD', '-1', '--', git.base]).strip()
+ # Could use %aI for author (ISO) date, rather than commit date (%c):
+ data['last-modified'] = git.run(['show', '-s', '--pretty=format:%cI',
+ data['version']]).split('T')[0]
+
+ try:
+ output.write(core.publish_string(
+ source=source.read(),
+ reader=QuipReader(),
+ parser_name='restructuredtext',
+ writer_name='html4css1',
+ writer=QuipWriter(),
+ # Allow Docutils traceback if there's an exception:
+ settings_overrides={'traceback': True, 'halt_level': 2}))
+ except DataError as what:
+ stderr.write('\n'.join(what.args) + '\nin %s\n' % filename)
+ return 1
+
+ return 0
+
+if __name__ == '__main__':
+ if len(sys.argv) < 2 or sys.argv[1] == '-':
+ here = os.path.split(sys.argv[0])[0]
+ sys.exit(main(os.path.join(here, 'gen-quip-0000.py'),
+ sys.stdin, sys.stdout, sys.stderr))
+ else:
+ with open(sys.argv[1]) as fd:
+ sys.exit(main(sys.argv[1], fd, sys.stdout, sys.stderr))
diff --git a/scripts/setup.mk b/scripts/setup.mk
new file mode 100644
index 0000000..74d088e
--- /dev/null
+++ b/scripts/setup.mk
@@ -0,0 +1,21 @@
+# Make-file for quips.pro to delegate to as:
+# make SRC=$$PWD setup
+default: setup
+WEB = $(SRC)/planetqt/website
+
+html:
+ mkdir -p html
+
+WEBDIRS = images
+$(WEBDIRS:%=html/%): html/%: html
+ [ -d $@ ] || ln -sf $(WEB)/images $(@D)
+
+WEBFILES = favicon.ico \
+ online.css style.css \
+ main.js extras.js modernizr.custom.84855.js \
+ cookie_small.png cookiebar-x.png list_arrow.png theqtcompany.png
+$(WEBFILES:%=html/%): html/%: html
+ [ -f $@ ] || ln -sf $(WEB)/$(@F) $(@D)
+
+setup: $(WEBFILES:%=html/%) $(WEBDIRS:%=html/%)
+ touch html/.ready
diff --git a/scripts/template.diff b/scripts/template.diff
new file mode 100644
index 0000000..0902e11
--- /dev/null
+++ b/scripts/template.diff
@@ -0,0 +1,73 @@
+--- planetqt/template.qt 2017-04-21 16:11:06.542439585 +0200
++++ scripts/template.html 2017-04-25 17:50:11.806838515 +0200
+@@ -9,8 +9,8 @@
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+
+- <title>Planet Qt</title>
+-
++ <title>%(title)s</title>
++ %(stylesheet)s
+ <link rel="shortcut icon" href="favicon.ico" />
+ <link href='http://fonts.googleapis.com/css?family=Open+Sans:700,300,400' rel='stylesheet' type='text/css'>
+ <link rel="stylesheet" type="text/css" href="online.css" />
+@@ -82,33 +82,14 @@
+ </div>
+ <div class="content">
+ <div class="wrapper">
+- <div class="content-column">
+- <div class="pages">
+- __paged_output_pages__
+- </div>
+- <div id="items">
+- __items__
+- </div>
+- <div class="pages">
+- __paged_output_pages__
+- </div>
+- </div>
+- <aside>
+- <img src="images/content-meteor.png" class="responsive-image" alt="Planet Qt" />
+- <a href="rss20.xml" class="rss-feed">RSS</a>
+- <h3>About Planet Qt</h3>
+- <p>The Qt Planet is an aggregator for Qt related blogs, regardless of who the author is. The opinions it contains are those of the respective author. This site is powered by <a href="http://offog.org/code/rawdog.html">Rawdog</a> and <a href="https://launchpad.net/rawdog-rss">Rawdog RSS</a>. Feed readers can read Planet Qt with <a href="rss20.xml">RSS</a>, <a href="foafroll.xml">FOAF</a> or <a href="opml.xml">OPML</a></p>
+- <h3>Join us</h3>
+- <ul class="icon-links">
+- <li class="qtproject-twitter"><a href="http://twitter.com/qtproject">@qtproject</a></li>
+- <li class="qtproject-gplus"><a href="https://plus.google.com/104580575722059274792">Qt Project</a></li>
+- <li class="qt-meetup"><a href="http://www.meetup.com/QtEverywhere/">Qt Meetups</a></li>
+- </ul>
+- <h3>Contributing</h3>
+- <p>All who blog in English about Qt can either add their complete RSS feed or a feed based on a specific category or tag for those blogging about multiple topics. All you need to do is clone the <a href="https://codereview.qt-project.org/#admin,project,www/planetqt,info">Planet Qt repository</a>, add your feed's address, and put your change up for <a href="http://wiki.qt.io/Code_Reviews">review</a>.</p>
+- <h3>Subscriptions</h3>
+- __feedlist__
+- </aside>
++ <h1>%(title)s</h1>
++ <ul>
++ <li><a href="./">QUIP Index</a></li>
++ <li><a href="quip-%(quipnum)s.html">QUIP %(quip)s</a></li>
++ </ul>
++<div class="context document">
++%(body)s
++</div>
+ </div>
+ </div>
+ <div id="footer">
+@@ -174,7 +155,7 @@
+ <a href="http://www.qt.io/about-us/" target="_blank" class="theqtcompany"></a>
+ <div class="footer-social clearfix">
+ <div class="facebook">
+- <iframe scrolling="no" frameborder="0" allowTransparency="true" src="//www.facebook.com/plugins/like.php?href=https%3A%2F%2Fwww.facebook.com%2Fqtbydigia&amp;width&amp;layout=button_count&amp;action=like&amp;show_faces=true&amp;share=false&amp;height=21" style="border:none;overflow:hidden;height:21px;"></iframe>
++ <iframe scrolling="no" frameborder="0" allowTransparency="true" src="//www.facebook.com/plugins/like.php?href=https%%3A%%2F%%2Fwww.facebook.com%%2Fqtbydigia&amp;width&amp;layout=button_count&amp;action=like&amp;show_faces=true&amp;share=false&amp;height=21" style="border:none;overflow:hidden;height:21px;"></iframe>
+ </div>
+ <div class="twitter">
+ <iframe id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" src="//platform.twitter.com/widgets/follow_button.33b190ea0cba008796487b65df7f6d8e.en.html#_=1414403615717&amp;id=twitter-widget-0&amp;lang=en&amp;screen_name=qtproject&amp;show_count=true&amp;show_screen_name=false&amp;size=m" class="twitter-follow-button twitter-follow-button" title="Twitter Follow Button" data-twttr-rendered="true" style="width: 160px; height: 20px;"></iframe>
+@@ -186,7 +167,7 @@
+ <div class="container clearfix no_discs">
+ <ul id="menu-footer-submenu" class="right clearfix"><li class="menu-item"><a href="http://www.qtworldsummit.com">Qt World Summit</a></li>
+ <li class="menu-item"><a title="Sign into your account." href="https://account.qt.io/login">Sign In</a></li>
+- <li class="menu-item"><a href="mailto:feedback@theqtcompany.com?Subject=Feedback%20about%20www.qt.io%20site">Feedback</a></li>
++ <li class="menu-item"><a href="mailto:feedback@theqtcompany.com?Subject=Feedback%%20about%%20www.qt.io%%20site">Feedback</a></li>
+ <li class="menu-item"><a href="http://www.qt.io/about-us">© 2015 The Qt Company</a></li>
+ </ul>
+ </div>