gerrit-dash-creator/gerrit_dash_creator/cmd/creator.py

222 lines
7.5 KiB
Python
Executable File

#!/usr/bin/env python3
#
# 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.
import argparse
import configparser
import io
import os
import os.path
import sys
from urllib import parse as urllib_parse
import jinja2
def escape(buff):
"""Because otherwise Firefox is a sad panda."""
return buff.replace(',', '%2c').replace('-', '%2D')
def generate_dashboard_url(dashboard):
"""Generate a dashboard URL from a given definition."""
try:
title = dashboard.get('dashboard', 'title')
except configparser.NoOptionError:
raise ValueError("option 'title' in section 'dashboard' not set")
try:
foreach = dashboard.get('dashboard', 'foreach')
except configparser.NoOptionError:
raise ValueError("option 'foreach' in section 'dashboard' not set")
try:
baseurl = dashboard.get('dashboard', 'baseurl')
except configparser.NoOptionError:
baseurl = 'https://review.opendev.org/#/dashboard/?'
url = baseurl
url += escape(urllib_parse.urlencode({'title': title,
'foreach': foreach}))
for section in dashboard.sections():
if not section.startswith('section'):
continue
try:
query = dashboard.get(section, 'query')
except configparser.NoOptionError:
raise ValueError("option 'query' in '%s' not set" % section)
title = section[9:-1]
encoded = escape(urllib_parse.urlencode({title: query}))
url += "&%s" % encoded
return url
def get_options():
"""Parse command line arguments and options."""
parser = argparse.ArgumentParser(
description='Create a Gerrit dashboard URL from specified dashboard '
'definition files')
parser.add_argument('dashboard_paths', nargs='+',
metavar='dashboard_path',
help='Path to a dashboard definition file or a '
'directory containing a set of dashboard '
'definition files with the file suffix .dash.')
parser.add_argument('--check-only', default=False, action="store_true",
help='Only check the syntax of the specified '
'dasbhoard files')
parser.add_argument('--template', default='single.txt',
help='Name of template')
# Find path to template_dir
# We need to support running with and without installation
if os.path.exists('templates'):
template_dir = 'templates'
elif os.path.exists('/usr/local/share/gerrit-dash-creator/templates'):
template_dir = '/usr/local/share/gerrit-dash-creator/templates'
else:
template_dir = os.path.join(sys.prefix, 'share',
'gerrit-dash-creator', 'templates')
parser.add_argument('--template-directory',
default=template_dir,
help='Directory to scan for template files')
parser.add_argument('--template-file', default=None,
help='Location of a specific template file')
parser.add_argument('--open', action='store_true',
help='Open the link in the default web browser')
return parser.parse_args()
def read_dashboard_file(dashboard_file):
"""Read and parse a dashboard definition from a specified file."""
if (not os.path.isfile(dashboard_file) or
not os.access(dashboard_file, os.R_OK)):
raise ValueError("dashboard file '%s' is missing or "
"is not readable" % dashboard_file)
dashboard = configparser.ConfigParser()
dashboard.read_file(open(dashboard_file))
return dashboard
def load_template(template_file=None, template_directory=None,
template_name=None):
"""Load the specified template."""
if template_file:
template_name = os.path.basename(template_file)
template_directory = os.path.dirname(os.path.abspath(template_file))
try:
loader = jinja2.FileSystemLoader(template_directory)
environment = jinja2.Environment(loader=loader)
template = environment.get_template(template_name)
except (jinja2.exceptions.TemplateError, IOError) as e:
print("error: opening template '%s' failed: %s" %
(template_name, e.__class__.__name__))
return
return template
def get_configuration(dashboard):
"""Returns the configuration of a dashboard as string."""
configuration = io.StringIO()
dashboard.write(configuration)
result = configuration.getvalue()
configuration.close()
return result
def generate_dashboard_urls(dashboards, template, open_browser=False):
"""Prints the dashboard URLs of a set of dashboards."""
result = 0
for dashboard_file in dashboards:
dashboard = dashboards[dashboard_file]
try:
url = generate_dashboard_url(dashboard)
except ValueError as e:
raise ValueError("generating dashboard '%s' failed: %s" %
(dashboard_file, e))
result = 1
continue
variables = {
'url': url,
'title': dashboard.get('dashboard', 'title') or None,
'description': dashboard.get('dashboard', 'description') or None,
'configuration': get_configuration(dashboard)
}
url = template.render(variables)
print(url)
if open_browser:
import webbrowser
webbrowser.open(url)
return result
def load_dashboards(paths):
"""Load specified dashboards from files or directories."""
dashboards = {}
for dashboard_path in paths:
dashboard_files = []
if os.path.isdir(dashboard_path):
for root, dirs, files in os.walk(dashboard_path):
for file in files:
if file.endswith('.dash'):
dashboard_files.append(os.path.join(root, file))
else:
dashboard_files.append(dashboard_path)
for dashboard_file in dashboard_files:
try:
dashboards[dashboard_file] = read_dashboard_file(
dashboard_file
)
except configparser.Error as e:
raise ValueError("dashboard file '%s' cannot be "
"parsed: %s" % (dashboard_file, e))
return dashboards
def main():
"""Entrypoint."""
opts = get_options()
template = None
if not opts.check_only:
template = load_template(
template_file=opts.template_file,
template_directory=opts.template_directory,
template_name=opts.template
)
try:
dashboards = load_dashboards(opts.dashboard_paths)
if not opts.check_only and template:
generate_dashboard_urls(dashboards, template,
open_browser=opts.open)
elif not opts.check_only and not template:
return 1
except ValueError as e:
print("error: %s" % e)
return 1
return 0
if __name__ == '__main__':
sys.exit(main())