~launchpad-pqm/launchpad/devel

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
150
151
152
#!/usr/bin/python2.4
#
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
#
# Generates documentation of mapping from remote bug tracker statuses
# to local, Launchpad statuses. Typically, this should be used to
# update the online documentation each time the status mappings are
# updated.
#
# See https://help.launchpad.net/BugStatuses
#

# XXX: GavinPanella 2008-12-03 bug=235434: This script is a temporary
# measure before moving the documentation of external bug status
# mapping tables into Launchpad proper. It is not tested and will
# remain so because it would be a mostly wasted effort. The impact of
# this is very low because it is infrequently run, and typically only
# by me, so I get to pick up the pieces.

import _pythonpath

import codecs
import sys

from datetime import datetime
from itertools import chain
from optparse import OptionParser

from canonical.launchpad.components.externalbugtracker import (
    BUG_TRACKER_CLASSES)


def generate_blank_lines(num):
    """Generate `num` blank lines."""
    for i in range(num):
        yield ''


def generate_page_header():
    """Generate the header for the page."""
    yield '<<Anchor(StatusTables)>>'
    yield '== Status mapping tables =='
    yield ''
    yield 'Last generated: %s.' % (
        datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC'),)


def generate_table_header(typ):
    """Generate a header for an individual table.

    :param typ: A member of `BugTrackerType`.
    """
    yield '<<Anchor(%s)>>' % (typ.name,)
    yield '=== %s ===' % (typ.title,)


def generate_table_grid(lookup, titles):
    """Simply return the table grid generated by the lookup tree."""
    return lookup.moinmoin_table(titles)


def generate_table(typ, cls):
    """Generate a table documenting the specified external bug tracker.

    :param typ: A member of `BugTrackerType`.
    :param cls: The `ExternalBugTracker` subclass corresponding to `typ`.
    """

    # Get the lookup tree.
    lookup = cls._status_lookup

    # Find or fabricate titles.
    titles = getattr(cls, '_status_lookup_titles', None)
    if titles is None:
        titles = ['Key %d' % (i + 1) for i in range(lookup.max_depth)]
    else:
        titles = list(titles)
    titles.append('Launchpad status')

    # Format the table.
    return chain(
        generate_table_header(typ),
        generate_blank_lines(1),
        generate_table_grid(lookup, titles))


def generate_documentable_classes():
    """Yield each class that has a mapping table defined."""
    for typ, cls in BUG_TRACKER_CLASSES.iteritems():
        if getattr(cls, '_status_lookup', None) is not None:
            yield typ, cls


def generate_tables():
    """Generate all the tables."""
    documentable_classes = sorted(
        generate_documentable_classes(),
        key=(lambda (typ, cls): typ.title))
    return chain(
        *(chain(generate_table(typ, cls),
                generate_blank_lines(2))
          for (typ, cls) in documentable_classes))


def generate_page():
    """Generate the MoinMoin-style page."""
    return chain(
        generate_page_header(),
        generate_blank_lines(2),
        generate_tables())


def write_page(outfile):
    """Write the page lines to `outfile`.

    :param outfile: A `file`-like object.
    """
    # By default, encode using UTF-8.
    write = codecs.getwriter('UTF-8')(outfile).write
    for line in generate_page():
        write(line)
        write('\n')


def get_option_parser():
    """Return the option parser for this program."""
    usage = "Usage: %prog [options]"
    parser = OptionParser(
        usage=usage, description=(
            "Generates MoinMoin-style tables to document the mapping of "
            "remote bug statuses to Launchpad statuses."))
    parser.add_option(
        "-o", "--file-out", dest="outfile", default='-', help=(
            "write data to OUTFILE"))
    return parser


def main(args):
    parser = get_option_parser()
    (options, args) = parser.parse_args(args)
    if len(args) > 0:
        parser.error("Incorrect number of arguments.")
    if options.outfile == '-':
        outfile = sys.stdout
    else:
        outfile = open(options.outfile, 'wb')
    write_page(outfile)


if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))