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