From 29b05952d7a5838f6c0ee777e95b72859e7e4972 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sat, 3 Jun 2023 17:56:08 +0200 Subject: [PATCH] Fix bugs reported by Paul Wise - fix internal server error when name is missing for non Debian member - fix unicode handling in urlbuilder --- ChangeLog | 2 + debianmemberportfolio/model/keyfinder.py | 57 +++-- .../model/keyringanalyzer.py | 158 +++++++------ debianmemberportfolio/model/urlbuilder.py | 111 +++++---- debianmemberportfolio/views.py | 214 +++++++++--------- 5 files changed, 297 insertions(+), 245 deletions(-) diff --git a/ChangeLog b/ChangeLog index 24fbc7d..8c45a78 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,8 @@ * add updated translations from Weblate * switch to Poetry for dependency management * describe translation workflow in developer documentation + * fix internal server error when name is missing for non Debian member (thanks Paul Wise for the report) + * fix unicode handling in urlbuilder (thanks Paul Wise for the report) 2022-09-24 Jan Dittberner * add updated translations from Weblate diff --git a/debianmemberportfolio/model/keyfinder.py b/debianmemberportfolio/model/keyfinder.py index c4b8a54..044648a 100644 --- a/debianmemberportfolio/model/keyfinder.py +++ b/debianmemberportfolio/model/keyfinder.py @@ -3,7 +3,7 @@ # # Debian Member Portfolio Service key finder module # -# Copyright © 2009-2022 Jan Dittberner +# Copyright © 2009-2023 Jan Dittberner # # This file is part of the Debian Member Portfolio Service. # @@ -26,37 +26,34 @@ given keyring. """ import logging -import time import sys +import time +from importlib import resources db = None -cachetimestamp = 0 +cache_timestamp = 0 def _get_keyring_cache(): - global db, cachetimestamp - if db is None or (time.time() - cachetimestamp) > 86300: + global db, cache_timestamp + if db is None or (time.time() - cache_timestamp) > 86300: import dbm - import pkg_resources import os.path - filename = pkg_resources.resource_filename(__name__, - 'keyringcache') - logging.debug('reading cache data from %s', filename) - assert ( - os.path.exists(filename + '.db') and - os.path.isfile(filename + '.db') - ) - db = dbm.open(filename, 'r') - cachetimestamp = time.time() + + dbm_filename = str(resources.files(__package__).joinpath("keyringcache.db")) + logging.debug("reading cache data from %s", dbm_filename) + assert os.path.exists(dbm_filename) and os.path.isfile(dbm_filename) + db = dbm.open(dbm_filename[: -len(".db")], "r") + cache_timestamp = time.time() return db def _get_cached(cachekey): cache = _get_keyring_cache() - logging.debug('cache lookup for %s', cachekey) + logging.debug("cache lookup for %s", cachekey) if cachekey in cache: - logging.debug('found entry %s', cache[cachekey]) - return cache[cachekey].decode('utf8') + logging.debug("found entry %s", cache[cachekey]) + return cache[cachekey].decode("utf8") return None @@ -65,7 +62,7 @@ def getFingerprintByEmail(email): Gets the fingerprints associated with the given email address if available. """ - return _get_cached('fpr:email:%s' % email) + return _get_cached("fpr:email:%s" % email) def getRealnameByEmail(email): @@ -73,7 +70,7 @@ def getRealnameByEmail(email): Gets the real names associated with the given email address if available. """ - return _get_cached('name:email:%s' % email) + return _get_cached("name:email:%s" % email) def getLoginByEmail(email): @@ -81,34 +78,34 @@ def getLoginByEmail(email): Gets the logins associated with the given email address if available. """ - return _get_cached('login:email:%s' % email) + return _get_cached("login:email:%s" % email) def getLoginByFingerprint(fpr): """ Gets the login associated with the given fingerprint if available. """ - return _get_cached('login:fpr:%s' % fpr) + return _get_cached("login:fpr:%s" % fpr) def _dump_cache(): cache = _get_keyring_cache() fprs = [] - for key in [key.decode('utf8') for key in list(cache.keys())]: - if key.startswith('email:fpr:'): - fpr = key.replace('email:fpr:', '') + for key in [key.decode("utf8") for key in list(cache.keys())]: + if key.startswith("email:fpr:"): + fpr = key.replace("email:fpr:", "") if not fpr in fprs: fprs.append(fpr) for fpr in fprs: login = getLoginByFingerprint(fpr) - email = _get_cached('email:fpr:%s' % fpr) - name = _get_cached('name:fpr:%s' % fpr) + email = _get_cached("email:fpr:%s" % fpr) + name = _get_cached("name:fpr:%s" % fpr) - print(fpr, login, ':') - print(' ', name, email) + print(fpr, login, ":") + print(" ", name, email) -if __name__ == '__main__': +if __name__ == "__main__": logging.basicConfig(stream=sys.stderr, level=logging.WARNING) _dump_cache() diff --git a/debianmemberportfolio/model/keyringanalyzer.py b/debianmemberportfolio/model/keyringanalyzer.py index 9ea3fab..0a007b8 100644 --- a/debianmemberportfolio/model/keyringanalyzer.py +++ b/debianmemberportfolio/model/keyringanalyzer.py @@ -3,7 +3,7 @@ # # Debian Member Portfolio Service application key ring analyzer tool # -# Copyright © 2009-2015 Jan Dittberner +# Copyright © 2009-2023 Jan Dittberner # # This file is part of the Debian Member Portfolio Service. # @@ -21,22 +21,21 @@ # along with this program. If not, see . # """ -This is a tool that analyzes GPG and PGP keyrings and stores the +This is a tool that analyzes GPG and PGP key rings and stores the retrieved data in a file database. The tool was inspired by Debian qa's carnivore. """ -import dbm -import pkg_resources -import glob import configparser +import dbm +import email.utils +import glob +import logging import os import os.path -import logging import subprocess import sys -import email.utils - +from importlib import resources CONFIG = configparser.ConfigParser() @@ -46,18 +45,18 @@ def _get_keyrings(): Gets the available keyring files from the keyring directory configured in portfolio.ini. """ - keyringdir = os.path.expanduser(CONFIG.get('DEFAULT', 'keyring.dir')) - logging.debug("keyring dir is %s", keyringdir) - keyrings = glob.glob(os.path.join(keyringdir, '*.gpg')) - keyrings.extend(glob.glob(os.path.join(keyringdir, '*.pgp'))) + keyring_dir = os.path.expanduser(CONFIG.get("DEFAULT", "keyring.dir")) + logging.debug("keyring dir is %s", keyring_dir) + keyrings = glob.glob(os.path.join(keyring_dir, "*.gpg")) + keyrings.extend(glob.glob(os.path.join(keyring_dir, "*.pgp"))) keyrings.sort() return keyrings def _parse_uid(uid): """ - Parse a uid of the form 'Real Name ' into email - and realname parts. + Parse an uid of the form 'Real Name ' into email + and real name parts. """ # First try with the Python library, but it doesn't always catch everything @@ -67,63 +66,65 @@ def _parse_uid(uid): if (not name) or (not mail): logging.debug("strange uid %s: '%s' - <%s>", uid, name, mail) # Try and do better than the python library - if not '@' in mail: + if "@" not in mail: uid = uid.strip() # First, strip comment - s = uid.find('(') - e = uid.find(')') + s = uid.find("(") + e = uid.find(")") if s >= 0 and e >= 0: - uid = uid[:s] + uid[e + 1:] - s = uid.find('<') - e = uid.find('>') + uid = uid[:s] + uid[e + 1 :] + s = uid.find("<") + e = uid.find(">") mail = None if s >= 0 and e >= 0: - mail = uid[s + 1:e] - uid = uid[:s] + uid[e + 1:] + mail = uid[s + 1 : e] + uid = uid[:s] + uid[e + 1 :] uid = uid.strip() - if not mail and uid.find('@') >= 0: + if not mail and uid.find("@") >= 0: mail, uid = uid, mail name = uid logging.debug("corrected: '%s' - <%s>", name, mail) - return (name, mail) + return name, mail -resultdict = {} + +result_dict = {} def _get_canonical(key): - if not key in resultdict: - resultdict[key] = [] + if key not in result_dict: + result_dict[key] = [] return key -def _add_to_result(key, newvalue): - logging.debug("adding %s: %s", key, newvalue) - thekey = _get_canonical(key) - if newvalue not in resultdict[thekey]: - resultdict[thekey].append(newvalue) +def _add_to_result(key, new_value): + logging.debug("adding %s: %s", key, new_value) + the_key = _get_canonical(key) + if new_value not in result_dict[the_key]: + result_dict[the_key].append(new_value) def _handle_mail(mail, fpr): - if mail.endswith('@debian.org'): - login = mail[0:-len('@debian.org')] - _add_to_result('login:email:%s' % mail, login) - _add_to_result('login:fpr:%s' % fpr, login) - _add_to_result('fpr:login:%s' % login, fpr) - _add_to_result('fpr:email:%s' % mail, fpr) - _add_to_result('email:fpr:%s' % fpr, mail) + if mail.endswith("@debian.org"): + login = mail[0 : -len("@debian.org")] + _add_to_result("login:email:%s" % mail, login) + _add_to_result("login:fpr:%s" % fpr, login) + _add_to_result("fpr:login:%s" % login, fpr) + _add_to_result("fpr:email:%s" % mail, fpr) + _add_to_result("email:fpr:%s" % fpr, mail) def _handle_uid(uid, fpr): + mail = None # Do stuff with 'uid' if uid: (uid, mail) = _parse_uid(uid) if mail: _handle_mail(mail, fpr) if uid: - _add_to_result('name:fpr:%s' % fpr, uid) + _add_to_result("name:fpr:%s" % fpr, uid) if mail: - _add_to_result('name:email:%s' % mail, uid) + _add_to_result("name:email:%s" % mail, uid) return fpr @@ -131,13 +132,13 @@ def process_gpg_list_keys_line(line, fpr): """ Process a line of gpg --list-keys --with-colon output. """ - items = line.split(':') - if items[0] == 'pub': + items = line.split(":") + if items[0] == "pub": return None - if items[0] == 'fpr': + if items[0] == "fpr": return items[9].strip() - if items[0] == 'uid': - if items[1] == 'r': + if items[0] == "uid": + if items[1] == "r": return fpr return _handle_uid(items[9].strip(), fpr) else: @@ -145,41 +146,54 @@ def process_gpg_list_keys_line(line, fpr): def process_keyrings(): - """Process the keyrings and store the extracted data in an anydbm - file.""" + """Process the keyrings and store the extracted data in an anydbm file.""" for keyring in _get_keyrings(): logging.debug("get data from %s", keyring) - proc = subprocess.Popen([ - "gpg", "--no-options", "--no-default-keyring", - "--homedir", os.path.expanduser( - CONFIG.get('DEFAULT', 'gnupghome')), - "--no-expensive-trust-checks", - "--keyring", keyring, "--list-keys", - "--with-colons", "--fixed-list-mode", "--with-fingerprint", - "--with-fingerprint"], - stdout=subprocess.PIPE) + proc = subprocess.Popen( + [ + "gpg", + "--no-options", + "--no-default-keyring", + "--homedir", + os.path.expanduser(CONFIG.get("DEFAULT", "gnupghome")), + "--no-expensive-trust-checks", + "--keyring", + keyring, + "--list-keys", + "--with-colons", + "--fixed-list-mode", + "--with-fingerprint", + "--with-fingerprint", + ], + stdout=subprocess.PIPE, + ) fpr = None for line in proc.stdout.readlines(): try: - line = line.decode('utf8') + line = line.decode("utf8") except UnicodeDecodeError: - line = line.decode('iso8859-1') + line = line.decode("iso8859-1") fpr = process_gpg_list_keys_line(line, fpr) - retcode = proc.wait() - if retcode != 0: - logging.error("subprocess ended with return code %d", retcode) - db = dbm.open(pkg_resources.resource_filename(__name__, - 'keyringcache'), 'c') - for key in resultdict: - db[key] = ":".join(resultdict[key]) + ret_code = proc.wait() + if ret_code != 0: + logging.error("subprocess ended with return code %d", ret_code) + dbm_filename = str( + resources.files("debianmemberportfolio.model").joinpath("keyringcache") + ) + db = dbm.open(dbm_filename, "c") + for key in result_dict: + db[key] = ":".join(result_dict[key]) db.close() -if __name__ == '__main__': +if __name__ == "__main__": logging.basicConfig(stream=sys.stderr, level=logging.WARNING) - CONFIG.read_string(pkg_resources.resource_string( - __name__, 'portfolio.ini').decode('utf8')) - gpghome = os.path.expanduser(CONFIG.get('DEFAULT', 'gnupghome')) - if not os.path.isdir(gpghome): - os.makedirs(gpghome, 0o700) + CONFIG.read_string( + resources.files("debianmemberportfolio.model") + .joinpath("portfolio.ini") + .read_text("utf8") + ) + gpg_home = os.path.expanduser(CONFIG.get("DEFAULT", "gnupghome")) + if not os.path.isdir(gpg_home): + os.makedirs(gpg_home, 0o700) process_keyrings() diff --git a/debianmemberportfolio/model/urlbuilder.py b/debianmemberportfolio/model/urlbuilder.py index d350021..0f180ab 100644 --- a/debianmemberportfolio/model/urlbuilder.py +++ b/debianmemberportfolio/model/urlbuilder.py @@ -3,7 +3,7 @@ # # Debian Member Portfolio Service url builder # -# Copyright © 2009-2022 Jan Dittberner +# Copyright © 2009-2023 Jan Dittberner # # This file is part of the Debian Member Portfolio Service. # @@ -28,36 +28,40 @@ portfolio.ini. from configparser import ConfigParser, InterpolationMissingOptionError from encodings.utf_8 import StreamReader as UTF8StreamReader - -import pkg_resources -from debianmemberportfolio.model import keyfinder +from importlib import resources from urllib.parse import quote_plus -from flask_babel import gettext as _, lazy_gettext as N_ +from debianmemberportfolio.model import keyfinder +from flask_babel import gettext as _ +from flask_babel import lazy_gettext as N_ my_config = ConfigParser() -my_config.read_file(UTF8StreamReader( - pkg_resources.resource_stream(__name__, 'portfolio.ini'))) +ref = resources.files("debianmemberportfolio.model").joinpath("portfolio.ini") +with ref.open("rb") as fp: + my_config.read_file(UTF8StreamReader(fp)) _FIELDNAMES_MAP = { - 'email': N_('Email address'), - 'name': N_('Name'), - 'openpgpfp': N_('OpenPGP fingerprint'), - 'username': N_('Debian user name'), - 'nonddemail': N_('Non Debian email address'), - 'salsausername': N_('Salsa user name'), + "email": N_("Email address"), + "name": N_("Name"), + "openpgpfp": N_("OpenPGP fingerprint"), + "username": N_("Debian user name"), + "nonddemail": N_("Non Debian email address"), + "salsausername": N_("Salsa user name"), } class DDPortfolioEntry(object): def __init__(self, config, section, key): self.name = key - self.optional = config.has_option(section, key + '.optional') and \ - config.getboolean(section, key + '.optional') or False - if config.has_option(section, key + '.type'): - self.type = config.get(section, key + '.type') + self.optional = ( + config.has_option(section, key + ".optional") + and config.getboolean(section, key + ".optional") + or False + ) + if config.has_option(section, key + ".type"): + self.type = config.get(section, key + ".type") else: - self.type = 'url' + self.type = "url" def _build_quoted_fields(fields): @@ -68,19 +72,19 @@ def _build_quoted_fields(fields): for key, value in fields.items(): if value is not None: if isinstance(value, str): - qfields[key] = quote_plus(value.encode('utf8')) + qfields[key] = quote_plus(value.encode("utf8")) elif isinstance(value, str): qfields[key] = quote_plus(value) else: qfields[key] = value - qfields[key] = str(qfields[key]).replace('%', '%%') + qfields[key] = str(qfields[key]).replace("%", "%%") - if 'openpgpfp' not in qfields: - fpr = keyfinder.getFingerprintByEmail(fields['email'].encode('utf8')) + if "openpgpfp" not in qfields: + fpr = keyfinder.getFingerprintByEmail(fields["email"]) if fpr: - qfields['openpgpfp'] = fpr[0] - qfields['firstchar'] = fields['email'][0].encode('utf8') - qfields['emailnoq'] = fields['email'].encode('utf8') + qfields["openpgpfp"] = fpr[0] + qfields["firstchar"] = fields["email"][0] + qfields["emailnoq"] = fields["email"] return qfields @@ -88,27 +92,50 @@ def build_urls(fields): """Build personalized URLs using the developer information in fields.""" data = [] - qfields = _build_quoted_fields(fields) - for section in [section.strip() for section in - my_config.get('DEFAULT', - 'urlbuilder.sections').split(',')]: - data.append(['section', section]) - if my_config.has_option(section, 'urls'): - for entry in ([ - DDPortfolioEntry(my_config, section, url) for url in - my_config.get(section, 'urls').split(',')]): + quoted_fields = _build_quoted_fields(fields) + for section in [ + section.strip() + for section in my_config.get("DEFAULT", "urlbuilder.sections").split(",") + ]: + data.append(["section", section]) + if my_config.has_option(section, "urls"): + for entry in [ + DDPortfolioEntry(my_config, section, url) + for url in my_config.get(section, "urls").split(",") + ]: try: data.append( - ['url', section, entry, - my_config.get(section, entry.name + '.pattern', - raw=False, vars=qfields)]) + [ + "url", + section, + entry, + my_config.get( + section, + entry.name + ".pattern", + raw=False, + vars=quoted_fields, + ), + ] + ) except InterpolationMissingOptionError as e: if not entry.optional: if e.reference in _FIELDNAMES_MAP: - data.append(['error', section, entry, - _('Missing input: %s') % - _(_FIELDNAMES_MAP[e.reference])]) + data.append( + [ + "error", + section, + entry, + _("Missing input: %s") + % _(_FIELDNAMES_MAP[e.reference]), + ] + ) else: - data.append(['error', section, entry, - _('Missing input: %s') % e.reference]) + data.append( + [ + "error", + section, + entry, + _("Missing input: %s") % e.reference, + ] + ) return data diff --git a/debianmemberportfolio/views.py b/debianmemberportfolio/views.py index 1d3ad54..49f44a1 100644 --- a/debianmemberportfolio/views.py +++ b/debianmemberportfolio/views.py @@ -3,7 +3,7 @@ # # Debian Member Portfolio Service views # -# Copyright © 2015-2022 Jan Dittberner +# Copyright © 2015-2023 Jan Dittberner # # This file is part of the Debian Member Portfolio Service. # @@ -23,11 +23,13 @@ import json import logging +from config import LANGUAGES from debianmemberportfolio import app, babel -from flask import g, make_response, request, render_template, abort +from flask import abort, g, make_response, render_template, request + # noinspection PyPep8Naming from flask_babel import lazy_gettext as N_ -from config import LANGUAGES + from .forms import DeveloperData, DeveloperDataRequest from .model import dddatabuilder from .model.urlbuilder import build_urls @@ -36,88 +38,93 @@ log = logging.getLogger(__name__) #: This dictionary defines groups of labeled portfolio items. _LABELS = { - 'overview': { - 'label': N_('Overview'), - 'ddpo': N_("Debian Member's Package Overview"), - 'alladdresses': N_("""Debian Member's Package Overview -... showing all email addresses"""), + "overview": { + "label": N_("Overview"), + "ddpo": N_("Debian Member's Package Overview"), + "alladdresses": N_( + """Debian Member's Package Overview +... showing all email addresses""" + ), }, - 'bugs': { - 'label': N_('Bugs'), - 'received': N_('''bugs received + "bugs": { + "label": N_("Bugs"), + "received": N_( + """bugs received (note: co-maintainers not listed, see \ #430986)'''), - 'reported': N_('bugs reported'), - 'usertags': N_('user tags'), - 'wnpp': N_('WNPP'), - 'correspondent': N_('correspondent for bugs'), - 'graph': N_('one year open bug history graph'), +bug=430986">#430986)""" + ), + "reported": N_("bugs reported"), + "usertags": N_("user tags"), + "wnpp": N_('WNPP'), + "correspondent": N_("correspondent for bugs"), + "graph": N_("one year open bug history graph"), }, - 'build': { - 'label': N_('Build'), - 'buildd': N_('buildd.d.o'), - 'igloo': N_('igloo'), + "build": { + "label": N_("Build"), + "buildd": N_("buildd.d.o"), + "igloo": N_("igloo"), }, - 'qa': { - 'label': N_('Quality Assurance'), - 'dmd': N_('maintainer dashboard'), - 'lintian': N_('lintian reports'), - 'lintianfull': N_('full lintian reports (i.e. including \ -"info"-level messages)'), - 'piuparts': N_('piuparts'), - 'janitor': N_('Debian Janitor'), + "qa": { + "label": N_("Quality Assurance"), + "dmd": N_("maintainer dashboard"), + "lintian": N_("lintian reports"), + "lintianfull": N_( + 'full lintian reports (i.e. including \ +"info"-level messages)' + ), + "piuparts": N_("piuparts"), + "janitor": N_("Debian Janitor"), }, - 'lists': { - 'label': N_('Mailing Lists'), - 'dolists': N_('lists.d.o'), - 'adolists': N_('lists.a.d.o'), + "lists": { + "label": N_("Mailing Lists"), + "dolists": N_("lists.d.o"), + "adolists": N_("lists.a.d.o"), }, - 'files': { - 'label': N_('Files'), - 'people': N_('people.d.o'), - 'oldpeople': N_('oldpeople'), + "files": { + "label": N_("Files"), + "people": N_("people.d.o"), + "oldpeople": N_("oldpeople"), }, - 'membership': { - 'label': N_('Membership'), - 'nm': N_('NM'), - 'dbfinger': N_('DB information via finger'), - 'db': N_('DB information via HTTP'), - 'salsa': N_('Salsa'), - 'wiki': N_('Wiki'), - 'forum': N_('Forum'), + "membership": { + "label": N_("Membership"), + "nm": N_("NM"), + "dbfinger": N_("DB information via finger"), + "db": N_("DB information via HTTP"), + "salsa": N_("Salsa"), + "wiki": N_("Wiki"), + "forum": N_("Forum"), }, - 'miscellaneous': { - 'label': N_('Miscellaneous'), - 'debtags': N_('debtags'), - 'planetname': N_('Planet Debian (name)'), - 'planetuser': N_('Planet Debian (username)'), - 'links': N_('links'), - 'website': N_('Debian website'), - 'search': N_('Debian search'), - 'gpgfinger': N_('OpenPGP public key via finger'), - 'gpgweb': N_('OpenPGP public key via HTTP'), - 'nm': N_('NM, AM participation'), - 'contrib': N_('Contribution information'), - 'repology': N_('Repology information'), + "miscellaneous": { + "label": N_("Miscellaneous"), + "debtags": N_("debtags"), + "planetname": N_("Planet Debian (name)"), + "planetuser": N_("Planet Debian (username)"), + "links": N_("links"), + "website": N_("Debian website"), + "search": N_("Debian search"), + "gpgfinger": N_("OpenPGP public key via finger"), + "gpgweb": N_("OpenPGP public key via HTTP"), + "nm": N_("NM, AM participation"), + "contrib": N_("Contribution information"), + "repology": N_("Repology information"), }, - 'ssh': { - 'label': N_('Information reachable via ssh (for Debian Members)'), - 'owndndoms': N_('owned debian.net domains'), - 'miainfo': N_('MIA database information'), - 'groupinfo': N_('Group membership information'), + "ssh": { + "label": N_("Information reachable via ssh (for Debian Members)"), + "owndndoms": N_("owned debian.net domains"), + "miainfo": N_( + 'MIA database information' + ), + "groupinfo": N_("Group membership information"), }, } #: list of field name tuples for Debian Maintainers -DM_TUPLES = (('name', 'name'), - ('openpgpfp', 'openpgpfp'), - ('nonddemail', 'email')) +DM_TUPLES = (("name", "name"), ("openpgpfp", "openpgpfp"), ("nonddemail", "email")) #: list of field name tuples for Debian Developers -DD_TUPLES = (('username', 'username'), - ('salsausername', 'username')) +DD_TUPLES = (("username", "username"), ("salsausername", "username")) def _get_label(section, url=None): @@ -125,8 +132,8 @@ def _get_label(section, url=None): if url: if url in _LABELS[section]: return _LABELS[section][url] - elif 'label' in _LABELS[section]: - return _LABELS[section]['label'] + elif "label" in _LABELS[section]: + return _LABELS[section]["label"] if url: return "%s.%s" % (section, url) return section @@ -142,70 +149,75 @@ def before_request(): g.locale = get_locale() -@app.route('/') +@app.route("/") def index(): form = DeveloperData() - return render_template('showform.html', form=form) + return render_template("showform.html", form=form) -@app.route('/result') +@app.route("/result") def urllist(): form = DeveloperData(request.values) if form.validate(): - fields = dddatabuilder.build_data(form.data['email']) + fields = dddatabuilder.build_data(form.data["email"]) form_data = form.data.copy() - if fields['type'] in (dddatabuilder.TYPE_DD, dddatabuilder.TYPE_DM): + if fields["type"] in (dddatabuilder.TYPE_DD, dddatabuilder.TYPE_DM): for dmtuple in DM_TUPLES: if not form_data[dmtuple[0]]: form_data[dmtuple[0]] = fields[dmtuple[1]] - if fields['type'] == dddatabuilder.TYPE_DD: + if fields["type"] == dddatabuilder.TYPE_DD: for ddtuple in DD_TUPLES: if not form_data[ddtuple[0]]: form_data[ddtuple[0]] = fields[ddtuple[1]] - if not form_data['wikihomepage']: - log.debug('generate wikihomepage from name') - form_data['wikihomepage'] = "".join([ - part.capitalize() for part in form_data['name'].split() - ]) + if not form_data["wikihomepage"] and form_data["name"]: + log.debug("generate wikihomepage from name") + form_data["wikihomepage"] = "".join( + [part.capitalize() for part in form_data["name"].split()] + ) data = build_urls(form_data) - if form_data['mode'] == 'json': - response = make_response(json.dumps(dict( - [("{}.{}".format(entry[1], entry[2].name), entry[3]) - for entry in data if entry[0] == 'url']))) - response.headers['Content-Type'] = 'application/json' + if form_data["mode"] == "json": + response = make_response( + json.dumps( + dict( + [ + ("{}.{}".format(entry[1], entry[2].name), entry[3]) + for entry in data + if entry[0] == "url" + ] + ) + ) + ) + response.headers["Content-Type"] = "application/json" return response for entry in data: - if entry[0] in ('url', 'error'): + if entry[0] in ("url", "error"): entry.append(_get_label(entry[1], entry[2].name)) - elif entry[0] == 'section': + elif entry[0] == "section": entry.append(_get_label(entry[1])) - return render_template('showurls.html', urldata=data) - return render_template('showform.html', form=form) + return render_template("showurls.html", urldata=data) + return render_template("showform.html", form=form) -@app.route('/htmlformhelper.js') +@app.route("/htmlformhelper.js") def formhelper_js(): - response = make_response(render_template('showformscript.js')) - response.headers['Content-Type'] = 'text/javascript; charset=utf-8' + response = make_response(render_template("showformscript.js")) + response.headers["Content-Type"] = "text/javascript; charset=utf-8" return response -@app.route('/showformscripts/fetchdddata/') +@app.route("/showformscripts/fetchdddata/") def fetchdddata(): form = DeveloperDataRequest(request.values) if form.validate(): - fields = dddatabuilder.build_data(form.data['email']) + fields = dddatabuilder.build_data(form.data["email"]) log.debug(fields) response = make_response(json.dumps(fields)) - response.headers['Content-Type'] = 'application/json' + response.headers["Content-Type"] = "application/json" return response - abort( - 400, - "\n".join(["%s: %s" % (key, form.errors[key]) for key in form.errors]) - ) + abort(400, "\n".join(["%s: %s" % (key, form.errors[key]) for key in form.errors]))