From 26f544d308ba5328352904617c7d0994461e4758 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 29 Aug 2019 21:40:26 +0200 Subject: make pretty --- talerbackoffice/backoffice/backoffice.py | 46 +++++++----- talerbackoffice/backoffice/templates/template.py | 54 +++++++------- talerbackoffice/helpers.py | 26 +++++-- talerbackoffice/talerconfig.py | 94 ++++++++++++++++-------- talerbackoffice/tests.py | 17 +++-- 5 files changed, 150 insertions(+), 87 deletions(-) diff --git a/talerbackoffice/backoffice/backoffice.py b/talerbackoffice/backoffice/backoffice.py index 8b6242b..ef74dd9 100644 --- a/talerbackoffice/backoffice/backoffice.py +++ b/talerbackoffice/backoffice/backoffice.py @@ -17,8 +17,6 @@ # # @author Florian Dold # @author Marcello Stanisci - - """ Implement URL handlers for backoffice logic. """ @@ -61,19 +59,23 @@ LANGUAGES = { 'ru': 'Russian', } + @babel.localeselector def get_locale(): - # If any "lang" component exists in the path, then - # it must be at the second position into the split array. - lang = flask.request.path.split("/")[1] - return lang if lang in LANGUAGES.keys() else "en" + # If any "lang" component exists in the path, then + # it must be at the second position into the split array. + lang = flask.request.path.split("/")[1] + return lang if lang in LANGUAGES.keys() else "en" + @app.context_processor def utility_processor(): def url(my_url): return join_urlparts(flask.request.script_root, my_url) + def env(name, default=None): return os.environ.get(name, default) + return dict(url=url, env=env) @@ -81,7 +83,10 @@ def utility_processor(): @app.route("/", defaults={"lang": "en"}) def index(lang): # 'lang' parameter not used. - return flask.render_template("templates/backoffice.html", instances=INSTANCES.split()) + return flask.render_template( + "templates/backoffice.html", instances=INSTANCES.split() + ) + @app.route("/javascript") def javascript_licensing(): @@ -92,10 +97,11 @@ def javascript_licensing(): def history(): qs = get_query_string().decode("utf-8") url = urljoin(BACKEND_URL, "history") - resp = requests.get(url, - params=dict(parse_qsl(qs)), - headers={"Authorization": - "ApiKey sandbox"}) + resp = requests.get( + url, + params=dict(parse_qsl(qs)), + headers={"Authorization": "ApiKey sandbox"} + ) if resp.status_code != 200: return backend_error(resp) return flask.jsonify(resp.json()), resp.status_code @@ -105,10 +111,11 @@ def history(): def track_transfer(): qs = get_query_string().decode("utf-8") url = urljoin(BACKEND_URL, "track/transfer") - resp = requests.get (url, - params=dict(parse_qsl(qs)), - headers={"Authorization": - "ApiKey sandbox"}) + resp = requests.get( + url, + params=dict(parse_qsl(qs)), + headers={"Authorization": "ApiKey sandbox"} + ) if resp.status_code != 200: return backend_error(resp) return flask.jsonify(resp.json()), resp.status_code @@ -118,10 +125,11 @@ def track_transfer(): def track_order(): qs = get_query_string().decode("utf-8") url = urljoin(BACKEND_URL, "track/transaction") - resp = requests.get(url, - params=dict(parse_qsl(qs)), - headers={"Authorization": - "ApiKey sandbox"}) + resp = requests.get( + url, + params=dict(parse_qsl(qs)), + headers={"Authorization": "ApiKey sandbox"} + ) if resp.status_code != 200: return backend_error(resp) return flask.jsonify(resp.json()), resp.status_code diff --git a/talerbackoffice/backoffice/templates/template.py b/talerbackoffice/backoffice/templates/template.py index eae04eb..aad9cf7 100755 --- a/talerbackoffice/backoffice/templates/template.py +++ b/talerbackoffice/backoffice/templates/template.py @@ -19,20 +19,24 @@ import os sys.path.append(os.getcwd()) import i18nfix -env = jinja2.Environment(loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), - extensions=["jinja2.ext.i18n"], - lstrip_blocks=True, - trim_blocks=True, - undefined=jinja2.StrictUndefined, - autoescape=False) +env = jinja2.Environment( + loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), + extensions=["jinja2.ext.i18n"], + lstrip_blocks=True, + trim_blocks=True, + undefined=jinja2.StrictUndefined, + autoescape=False +) -langs_full = {"en": "English", - "fr": "Français", - "it": "Italiano", - "es": "Español", - "de": "Deutsch", - "ru": "Ру́сский язы́к", - "pt": "Português"} +langs_full = { + "en": "English", + "fr": "Français", + "it": "Italiano", + "es": "Español", + "de": "Deutsch", + "ru": "Ру́сский язы́к", + "pt": "Português" +} for in_file in glob.glob("*.j2"): name, ext = re.match(r"(.*)\.([^.]+)$", in_file.rstrip(".j2")).groups() @@ -49,7 +53,7 @@ for in_file in glob.glob("*.j2"): def svg_localized(filename): lf = filename + "." + locale + ".svg" - if "en" == locale or not os.path.isfile (lf): + if "en" == locale or not os.path.isfile(lf): return "../" + filename + ".svg" else: return "../" + lf @@ -62,23 +66,23 @@ for in_file in glob.glob("*.j2"): for l in glob.glob("locale/*/"): locale = os.path.basename(l[:-1]) - tr = gettext.translation("messages", - localedir="locale", - languages=[locale]) + tr = gettext.translation( + "messages", localedir="locale", languages=[locale] + ) tr.gettext = i18nfix.wrap_gettext(tr.gettext) env.install_gettext_translations(tr, newstyle=True) - content = tmpl.render( - lang=locale, - lang_full=langs_full[locale], - url=url, - self_localized=self_localized, - url_localized=url_localized, - svg_localized=svg_localized, - filename=name + "." + ext) + lang=locale, + lang_full=langs_full[locale], + url=url, + self_localized=self_localized, + url_localized=url_localized, + svg_localized=svg_localized, + filename=name + "." + ext + ) out_name = "./" + locale + "/" + in_file.rstrip(".j2") os.makedirs("./" + locale, exist_ok=True) with codecs.open(out_name, "w", "utf-8") as f: diff --git a/talerbackoffice/helpers.py b/talerbackoffice/helpers.py index d574126..46e4fdd 100644 --- a/talerbackoffice/helpers.py +++ b/talerbackoffice/helpers.py @@ -33,11 +33,13 @@ FRACTION_BASE = 1e8 if not NDIGITS: NDIGITS = 2 + class MissingParameterException(Exception): def __init__(self, param): self.param = param super().__init__() + def amount_to_float(amount): return amount['value'] + (float(amount['fraction']) / float(FRACTION_BASE)) @@ -82,7 +84,9 @@ def make_url(page, *query_params): def expect_parameter(name, alt=None): value = flask.request.args.get(name, None) if value is None and alt is None: - LOGGER.error("Missing parameter '%s' from '%s'." % (name, flask.request.args)) + LOGGER.error( + "Missing parameter '%s' from '%s'." % (name, flask.request.args) + ) raise MissingParameterException(name) return value if value else alt @@ -90,12 +94,20 @@ def expect_parameter(name, alt=None): def get_query_string(): return flask.request.query_string + def backend_error(requests_response): - LOGGER.error("Backend error: status code: " - + str(requests_response.status_code)) + LOGGER.error( + "Backend error: status code: " + str(requests_response.status_code) + ) try: - return flask.jsonify(requests_response.json()), requests_response.status_code + return flask.jsonify( + requests_response.json() + ), requests_response.status_code except json.decoder.JSONDecodeError: - LOGGER.error("Backend error (NO JSON returned): status code: " - + str(requests_response.status_code)) - return flask.jsonify(dict(error="Backend died, no JSON got from it")), 502 + LOGGER.error( + "Backend error (NO JSON returned): status code: " + + str(requests_response.status_code) + ) + return flask.jsonify( + dict(error="Backend died, no JSON got from it") + ), 502 diff --git a/talerbackoffice/talerconfig.py b/talerbackoffice/talerconfig.py index 4a4b2d1..5c9f300 100644 --- a/talerbackoffice/talerconfig.py +++ b/talerbackoffice/talerconfig.py @@ -13,7 +13,6 @@ # TALER; see the file COPYING. If not, see # # @author Florian Dold - """ Parse GNUnet-style configurations in pure Python """ @@ -37,9 +36,11 @@ try: except ImportError: pass + class ConfigurationError(Exception): pass + class ExpansionSyntaxError(Exception): pass @@ -66,7 +67,7 @@ def expand(var, getter): end += 1 if balance != 0: raise ExpansionSyntaxError("unbalanced parentheses") - piece = var[start+2:end-1] + piece = var[start + 2:end - 1] if piece.find(":-") > 0: varname, alt = piece.split(":-", 1) replace = getter(varname) @@ -79,16 +80,15 @@ def expand(var, getter): replace = var[start:end] else: end = start + 2 - while end < len(var) and var[start+1:end+1].isalnum(): + while end < len(var) and var[start + 1:end + 1].isalnum(): end += 1 - varname = var[start+1:end] + varname = var[start + 1:end] replace = getter(varname) if replace is None: replace = var[start:end] result = result + replace pos = end - return result + var[pos:] @@ -97,12 +97,15 @@ class OptionDict(collections.defaultdict): self.config = weakref.ref(config) self.section_name = section_name super().__init__() + def __missing__(self, key): entry = Entry(self.config(), self.section_name, key) self[key] = entry return entry + def __getitem__(self, chunk): return super().__getitem__(chunk.lower()) + def __setitem__(self, chunk, value): super().__setitem__(chunk.lower(), value) @@ -112,8 +115,10 @@ class SectionDict(collections.defaultdict): value = OptionDict(self, key) self[key] = value return value + def __getitem__(self, chunk): return super().__getitem__(chunk.lower()) + def __setitem__(self, chunk, value): super().__setitem__(chunk.lower(), value) @@ -141,11 +146,16 @@ class Entry: if self.value is None: if warn: if default is not None: - LOGGER.warning("Configuration is missing option '%s' in section '%s',\ - falling back to '%s'", self.option, self.section, default) + LOGGER.warning( + "Configuration is missing option '%s' in section '%s',\ + falling back to '%s'", self.option, + self.section, default + ) else: - LOGGER.warning("Configuration ** is missing option '%s' in section '%s'", - self.option.upper(), self.section.upper()) + LOGGER.warning( + "Configuration ** is missing option '%s' in section '%s'", + self.option.upper(), self.section.upper() + ) return default return self.value @@ -210,15 +220,18 @@ class TalerConfig: def value_string(self, section, option, **kwargs): return self.sections[section][option].value_string( - kwargs.get("default"), kwargs.get("required"), kwargs.get("warn")) + kwargs.get("default"), kwargs.get("required"), kwargs.get("warn") + ) def value_filename(self, section, option, **kwargs): return self.sections[section][option].value_filename( - kwargs.get("default"), kwargs.get("required"), kwargs.get("warn")) + kwargs.get("default"), kwargs.get("required"), kwargs.get("warn") + ) def value_int(self, section, option, **kwargs): return self.sections[section][option].value_int( - kwargs.get("default"), kwargs.get("required"), kwargs.get("warn")) + kwargs.get("default"), kwargs.get("required"), kwargs.get("warn") + ) def load_defaults(self): base_dir = os.environ.get("TALER_BASE_CONFIG") @@ -274,36 +287,51 @@ class TalerConfig: continue if line.startswith("["): if not line.endswith("]"): - LOGGER.error("invalid section header in line %s: %s", - lineno, repr(line)) + LOGGER.error( + "invalid section header in line %s: %s", lineno, + repr(line) + ) section_name = line.strip("[]").strip().strip('"') current_section = section_name continue if current_section is None: - LOGGER.error("option outside of section in line %s: %s", lineno, repr(line)) + LOGGER.error( + "option outside of section in line %s: %s", lineno, + repr(line) + ) continue pair = line.split("=", 1) if len(pair) != 2: - LOGGER.error("invalid option in line %s: %s", lineno, repr(line)) + LOGGER.error( + "invalid option in line %s: %s", lineno, repr(line) + ) key = pair[0].strip() value = pair[1].strip() if value.startswith('"'): value = value[1:] if not value.endswith('"'): - LOGGER.error("mismatched quotes in line %s: %s", lineno, repr(line)) + LOGGER.error( + "mismatched quotes in line %s: %s", lineno, + repr(line) + ) else: value = value[:-1] - entry = Entry(self.sections, current_section, key, - value=value, filename=filename, lineno=lineno) + entry = Entry( + self.sections, + current_section, + key, + value=value, + filename=filename, + lineno=lineno + ) sections[current_section][key] = entry except FileNotFoundError: LOGGER.error("Configuration file (%s) not found", filename) sys.exit(3) - def dump(self): for kv_section in self.sections.items(): - print("[%s]" % (kv_section[1].section_name,)) + print("[%s]" % (kv_section[1].section_name, )) for kv_option in kv_section[1].items(): print("%s = %s # %s" % \ (kv_option[1].option, @@ -320,14 +348,22 @@ if __name__ == "__main__": import argparse PARSER = argparse.ArgumentParser() - PARSER.add_argument("--section", "-s", dest="section", - default=None, metavar="SECTION") - PARSER.add_argument("--option", "-o", dest="option", - default=None, metavar="OPTION") - PARSER.add_argument("--config", "-c", dest="config", - default=None, metavar="FILE") - PARSER.add_argument("--filename", "-f", dest="expand_filename", - default=False, action='store_true') + PARSER.add_argument( + "--section", "-s", dest="section", default=None, metavar="SECTION" + ) + PARSER.add_argument( + "--option", "-o", dest="option", default=None, metavar="OPTION" + ) + PARSER.add_argument( + "--config", "-c", dest="config", default=None, metavar="FILE" + ) + PARSER.add_argument( + "--filename", + "-f", + dest="expand_filename", + default=False, + action='store_true' + ) ARGS = PARSER.parse_args() TC = TalerConfig.from_file(ARGS.config) diff --git a/talerbackoffice/tests.py b/talerbackoffice/tests.py index d7b3949..a03ae3c 100644 --- a/talerbackoffice/tests.py +++ b/talerbackoffice/tests.py @@ -9,21 +9,21 @@ from bs4 import BeautifulSoup TC = TalerConfig.from_env() CURRENCY = TC["taler"]["currency"].value_string(required=True) + class BackofficeTestCase(unittest.TestCase): def setUp(self): backoffice.app.testing = True self.app = backoffice.app.test_client() + # Check whether the homepage UI shows the instances # in the drop-down menu. Instances are read from the # environment. def test_instances_are_shown(self): response = self.app.get("/") soup = BeautifulSoup(response.data, "html.parser") - self.assertEqual("MusicShop", - soup.find("select").option.contents[0]) + self.assertEqual("MusicShop", soup.find("select").option.contents[0]) self.assertEqual(response.status_code, 200) - @patch("requests.get") def test_track_order(self, mocked_get): ret = MagicMock() @@ -34,7 +34,8 @@ class BackofficeTestCase(unittest.TestCase): mocked_get.assert_called_with( "http://backend.example.com/track/transaction", params=dict(para1='1', para2='2'), - headers={"Authorization": "ApiKey sandbox"}) + headers={"Authorization": "ApiKey sandbox"} + ) # check data is returned verbatim from the backend self.assertEqual(response.data, b'{}\n') @@ -48,11 +49,11 @@ class BackofficeTestCase(unittest.TestCase): mocked_get.assert_called_with( "http://backend.example.com/track/transfer", params=dict(para1='1', para2='2'), - headers={"Authorization": "ApiKey sandbox"}) + headers={"Authorization": "ApiKey sandbox"} + ) # check data is returned verbatim from the backend self.assertEqual(response.data, b'{}\n') - @patch("requests.get") def test_history(self, mocked_get): ret = MagicMock() @@ -63,7 +64,8 @@ class BackofficeTestCase(unittest.TestCase): mocked_get.assert_called_with( "http://backend.example.com/history", params=dict(para1='1', para2='2'), - headers={"Authorization": "ApiKey sandbox"}) + headers={"Authorization": "ApiKey sandbox"} + ) self.assertEqual(response.data, b'{}\n') def test_javascript_license_page(self): @@ -71,5 +73,6 @@ class BackofficeTestCase(unittest.TestCase): soup = BeautifulSoup(response.data, "html.parser") self.assertEqual(soup.body.table["id"], "jslicense-labels1") + if __name__ == "__main__": unittest.main() -- cgit v1.2.3