7ca7ed6d6e73cee5d6c6e024d2b39a7a193ca7cd
[docs.git] / docs / ext / odl-jira.py
1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2021 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3 #
4 # This program and the accompanying materials are made available under the
5 # terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 # and is available at http://www.eclipse.org/legal/epl-v10.html
7 """
8
9 Embeds a simple table with issues.
10
11 """
12
13 from docutils import nodes
14 from docutils.parsers.rst import directives, Directive
15 from jira import JIRA
16 import re
17 import sphinx
18
19 __copyright__ = "Copyright(c) 2021 PANTHEON.tech, s.r.o."
20 __license__ = "Eclipse Public License v1.0"
21
22 class JiraFixedIssuesDirective(Directive):
23     """
24     JIRA Fixed Issues directive
25     """
26     has_content = True
27     required_arguments = 0
28     optional_arguments = 0
29
30     option_spec = {
31         "project": directives.unchanged_required,
32         "versions": directives.unchanged_required,
33     }
34
35     def run(self):
36         jira = JIRA(server="https://jira.opendaylight.org")
37         prj = jira.project(self.options.get('project'))
38         (from_ver, to_ver) = self.options.get('versions').split('-', 1)
39
40         versions = set()
41         for ver in jira.project_versions(prj):
42             if ver.name >= from_ver and ver.name <= to_ver:
43                 versions.add(ver.name)
44         versions = ", ".join(versions)
45
46         # FIXME: this is not quite nice: can we emit the table markup directly
47         table = [
48             '.. list-table:: Issues resolved in versions %s through %s' % (from_ver, to_ver),
49             '   :class: datatable',
50             '   :header-rows: 1',
51             '   :widths: auto',
52             '',
53             '   * - Type',
54             '     - Key',
55             '     - Summary',
56             '     - Resolution',
57             '     - Fix Version(s)',
58         ]
59
60         issues = jira.search_issues('project = %s AND resolution is not EMPTY AND fixVersion in (%s) ORDER BY type ASC' % (prj, versions))
61         for issue in issues:
62             # Groom fixVersions
63             fixVersions = set()
64             for version in issue.fields.fixVersions:
65                 fixVersions.add(version.name)
66             fixVersions = list(fixVersions)
67             fixVersions.sort()
68
69             table.append('   * - .. image:: %s' % issue.fields.issuetype.iconUrl)
70             table.append('          :align: center')
71             table.append('          :alt: %s' % issue.fields.issuetype.name)
72             table.append('     - `%s <https://jira.opendaylight.org/browse/%s>`_' % (issue.key, issue.key))
73             table.append('     - %s' % issue.fields.summary)
74             table.append('     - %s' % issue.fields.resolution)
75             table.append('     - %s' % ", ".join(fixVersions))
76
77         table.append('')
78
79         for idx, line in enumerate(table):
80             self.content.data.insert(idx, line)
81             self.content.items.insert(idx, (None, idx))
82
83         node = nodes.container()
84         self.state.nested_parse(self.content, self.content_offset, node)
85         return node.children
86
87 class JiraKnownIssuesDirective(Directive):
88     """
89     JIRA Known Issues directive
90     """
91     has_content = True
92     required_arguments = 0
93     optional_arguments = 0
94
95     option_spec = {
96         "project": directives.unchanged_required,
97         "versions": directives.unchanged_required,
98     }
99
100     def run(self):
101         jira = JIRA(server="https://jira.opendaylight.org")
102         prj = jira.project(self.options.get('project'))
103         (from_ver, to_ver) = self.options.get('versions').split('-', 1)
104
105         versions = set()
106         for ver in jira.project_versions(prj):
107             if ver.name >= from_ver and ver.name <= to_ver:
108                 versions.add(ver.name)
109         versions = ", ".join(versions)
110
111         # FIXME: this is not quite nice: can we emit the table markup directly
112         table = [
113             '.. list-table:: Issues affecting versions %s through %s' % (from_ver, to_ver),
114             '   :class: datatable',
115             '   :header-rows: 1',
116             '   :widths: auto',
117             '',
118             '   * - Type',
119             '     - Key',
120             '     - Summary',
121             '     - Status',
122             '     - Affected Version(s)',
123             '     - Fix Version(s)',
124         ]
125
126         issues = jira.search_issues('project = %s AND affectedVersion in (%s) AND fixVersion NOT in (%s) ORDER BY type ASC' % (prj, versions, versions))
127         for issue in issues:
128             # Groom fixVersions
129             fixVersions = set()
130             for version in issue.fields.fixVersions:
131                 fixVersions.add(version.name)
132             fixVersions = list(fixVersions)
133             fixVersions.sort()
134
135             # Groom affectedVersions
136             affectedVersions = set()
137             for version in issue.fields.versions:
138                 affectedVersions.add(version.name)
139             affectedVersions = list(affectedVersions)
140             affectedVersions.sort()
141
142             table.append('   * - .. image:: %s' % issue.fields.issuetype.iconUrl)
143             table.append('          :align: center')
144             table.append('          :alt: %s' % issue.fields.issuetype.name)
145             table.append('     - `%s <https://jira.opendaylight.org/browse/%s>`_' % (issue.key, issue.key))
146             table.append('     - %s' % issue.fields.summary)
147             table.append('     - %s' % issue.fields.status)
148             table.append('     - %s' % ", ".join(affectedVersions))
149             table.append('     - %s' % ", ".join(fixVersions))
150
151         table.append('')
152
153         for idx, line in enumerate(table):
154             self.content.data.insert(idx, line)
155             self.content.items.insert(idx, (None, idx))
156
157         node = nodes.container()
158         self.state.nested_parse(self.content, self.content_offset, node)
159         return node.children
160
161 def setup(app):
162     """
163     :type app: sphinx.application.Sphinx
164     """
165     app.add_directive('jira_fixed_issues', JiraFixedIssuesDirective)
166     app.add_directive('jira_known_issues', JiraKnownIssuesDirective)
167
168     # https://datatables.net/ improvements to tables
169     app.add_css_file("https://cdn.datatables.net/1.11.2/css/jquery.dataTables.min.css")
170     app.add_js_file("https://cdn.datatables.net/1.11.2/js/jquery.dataTables.min.js")
171     app.add_js_file(None, **{"body": "$(document).ready( function () { $('table.datatable').DataTable(); } );", "type": "text/javascript", "class": "init"})
172
173     return {
174         'version': '0.1',
175         'parallel_read_safe': True,
176         'parallel_write_safe': True,
177     }
178