From 62118eb9531e11c38cc6cebb1cc2f7255d755ffc Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 18 Jan 2018 18:32:45 +0100 Subject: use new tipping API, fix various little things --- talersurvey/survey/amount.py | 135 -------------------------------- talersurvey/survey/survey.py | 66 ++++++++-------- talersurvey/survey/templates/base.html | 10 +-- talersurvey/survey/templates/index.html | 2 +- 4 files changed, 38 insertions(+), 175 deletions(-) delete mode 100644 talersurvey/survey/amount.py (limited to 'talersurvey') diff --git a/talersurvey/survey/amount.py b/talersurvey/survey/amount.py deleted file mode 100644 index 46e3446..0000000 --- a/talersurvey/survey/amount.py +++ /dev/null @@ -1,135 +0,0 @@ -# This file is part of TALER -# (C) 2017 TALER SYSTEMS -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -# @author Marcello Stanisci -# @version 0.1 -# @repository https://git.taler.net/copylib.git/ -# This code is "copylib", it is versioned under the Git repository -# mentioned above, and it is meant to be manually copied into any project -# which might need it. - -class CurrencyMismatch(Exception): - def __init__(self, curr1, curr2): - super(CurrencyMismatch, self).__init__( - "%s vs %s" % (curr1, curr2)) - -class BadFormatAmount(Exception): - def __init__(self, faulty_str): - super(BadFormatAmount, self).__init__( - "Bad format amount: " + faulty_str) - -class Amount: - # How many "fraction" units make one "value" unit of currency - # (Taler requires 10^8). Do not change this 'constant'. - @staticmethod - def _fraction(): - return 10 ** 8 - - @staticmethod - def _max_value(): - return (2 ** 53) - 1 - - def __init__(self, currency, value=0, fraction=0): - # type: (str, int, int) -> Amount - assert value >= 0 and fraction >= 0 - self.value = value - self.fraction = fraction - self.currency = currency - self.__normalize() - assert self.value <= Amount._max_value() - - # Normalize amount - def __normalize(self): - if self.fraction >= Amount._fraction(): - self.value += int(self.fraction / Amount._fraction()) - self.fraction = self.fraction % Amount._fraction() - - # Parse a string matching the format "A:B.C" - # instantiating an amount object. - @classmethod - def parse(cls, amount_str): - exp = r'^\s*([-_*A-Za-z0-9]+):([0-9]+)\.([0-9]+)\s*$' - import re - parsed = re.search(exp, amount_str) - if not parsed: - raise BadFormatAmount(amount_str) - value = int(parsed.group(2)) - fraction = 0 - for i, digit in enumerate(parsed.group(3)): - fraction += int(int(digit) * (Amount._fraction() / 10 ** (i+1))) - return cls(parsed.group(1), value, fraction) - - # Comare two amounts, return: - # -1 if a < b - # 0 if a == b - # 1 if a > b - @staticmethod - def cmp(am1, am2): - if am1.currency != am2.currency: - raise CurrencyMismatch(am1.currency, am2.currency) - if am1.value == am2.value: - if am1.fraction < am2.fraction: - return -1 - if am1.fraction > am2.fraction: - return 1 - return 0 - if am1.value < am2.value: - return -1 - return 1 - - def set(self, currency, value=0, fraction=0): - self.currency = currency - self.value = value - self.fraction = fraction - - # Add the given amount to this one - def add(self, amount): - if self.currency != amount.currency: - raise CurrencyMismatch(self.currency, amount.currency) - self.value += amount.value - self.fraction += amount.fraction - self.__normalize() - - # Subtract passed amount from this one - def subtract(self, amount): - if self.currency != amount.currency: - raise CurrencyMismatch(self.currency, amount.currency) - if self.fraction < amount.fraction: - self.fraction += Amount._fraction() - self.value -= 1 - if self.value < amount.value: - raise ValueError('self is lesser than amount to be subtracted') - self.value -= amount.value - self.fraction -= amount.fraction - - # Dump string from this amount, will put 'ndigits' numbers - # after the dot. - def stringify(self, ndigits): - assert ndigits > 0 - ret = '%s:%s.' % (self.currency, str(self.value)) - fraction = self.fraction - while ndigits > 0: - ret += str(int(fraction / (Amount._fraction() / 10))) - fraction = (fraction * 10) % (Amount._fraction()) - ndigits -= 1 - return ret - - # Dump the Taler-compliant 'dict' amount - def dump(self): - return dict(value=self.value, - fraction=self.fraction, - currency=self.currency) diff --git a/talersurvey/survey/survey.py b/talersurvey/survey/survey.py index 6c2f4c8..d068f66 100644 --- a/talersurvey/survey/survey.py +++ b/talersurvey/survey/survey.py @@ -48,55 +48,53 @@ def backend_error(requests_response): @app.context_processor def utility_processor(): - - def join_urlparts(*parts): - ret = "" - part = 0 - while part < len(parts): - buf = parts[part] - part += 1 - if ret.endswith("/"): - buf = buf.lstrip("/") - elif ret and not buf.startswith("/"): - buf = "/" + buf - ret += buf - return ret - - 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) -@app.route("/tip-pickup", methods=["POST"]) -def pick(): - request_body = flask.request.get_json() - resp = requests.post(urljoin(BACKEND_URL, "tip-pickup"), - json=request_body) +def err_abort(abort_status_code, **params): + t = flask.render_template("templates/error.html", **params) + flask.abort(flask.make_response(t, abort_status_code)) + + +def backend_post(endpoint, json): + try: + resp = requests.post(urljoin(BACKEND_URL, endpoint), json=json) + except requests.ConnectionError: + err_abort(500, message="Could not establish connection to backend") + try: + response_json = resp.json() + except ValueError: + err_abort(500, message="Could not parse response from backend", + status_code=resp.status_code) if resp.status_code != 200: - return backend_error(resp) - response_body = resp.json() - return flask.jsonify(response_body) + err_abort(500, message="Backend returned error status", + json=response_json, status_code=resp.status_code) + return response_json + + +@app.errorhandler(Exception) +def internal_error(e): + return flask.render_template("templates/error.html", + message="Internal error", + stack=traceback.format_exc()) + @app.route("/submit-survey", methods=["POST"]) def submit_survey(): tip_spec = dict(pickup_url=urljoin(flask.request.base_url, "/tip-pickup"), - amount=Amount(CURRENCY, 1).dump(), + amount=CURRENCY + ":1.0", next_url=os.environ.get("TALER_ENV_URL_INTRO", "https://taler.net/"), instance="default", justification="Payment methods survey") - resp = requests.post(urljoin(BACKEND_URL, "tip-authorize"), - json=tip_spec) - if resp.status_code != 200: - return backend_error(resp) + resp = backend_post("tip-authorize", tip_spec) - response = flask.make_response( - flask.render_template("templates/wait.html", success=True), - 402) - response.headers["X-Taler-Tip"] = resp.json()["tip_token"] + if resp.get("tip_redirect_url"): + flask.redirect(resp["tip_redirect_url"] - return response + err_abort(500, message="Tipping failed, unexpected backend response", + json=resp) @app.route("/", methods=["GET"]) diff --git a/talersurvey/survey/templates/base.html b/talersurvey/survey/templates/base.html index 5dbf182..e5cf03c 100644 --- a/talersurvey/survey/templates/base.html +++ b/talersurvey/survey/templates/base.html @@ -18,10 +18,10 @@ Taler Survey Demo - - - - + + + + {% block styles %}{% endblock %} {% block scripts %}{% endblock %} @@ -42,7 +42,7 @@
- +
-
+ What do you prefer?