diff options
| author | Alexis Metaireau <alexis@notmyidea.org> | 2017-07-07 00:06:56 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-07-07 00:06:56 +0200 |
| commit | 3a4282fd75e3b3317b2b08b4aa2e6ac154310e73 (patch) | |
| tree | 9470c907ba1f884246af87d26d55c3aaac6d6dc5 /ihatemoney/utils.py | |
| parent | 0e374cd5e0ef5a9be67084365f91de2ab84f636c (diff) | |
| download | ihatemoney-mirror-3a4282fd75e3b3317b2b08b4aa2e6ac154310e73.zip ihatemoney-mirror-3a4282fd75e3b3317b2b08b4aa2e6ac154310e73.tar.gz ihatemoney-mirror-3a4282fd75e3b3317b2b08b4aa2e6ac154310e73.tar.bz2 | |
Absolute imports & some other improvements (#243)
* Use absolute imports and rename package to ihatemoney
* Add a ihatemoney command
* Factorize application creation logic
* Refactor the tests
* Update the wsgi.py module with the new create_app() function
* Fix some styling thanks to Flake8.
* Automate Flake8 check in the CI.
Diffstat (limited to 'ihatemoney/utils.py')
| -rw-r--r-- | ihatemoney/utils.py | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/ihatemoney/utils.py b/ihatemoney/utils.py new file mode 100644 index 0000000..4e79a37 --- /dev/null +++ b/ihatemoney/utils.py @@ -0,0 +1,133 @@ +import base64 +import re + +from io import BytesIO, StringIO +from jinja2 import filters +from json import dumps +from flask import redirect +from werkzeug.routing import HTTPException, RoutingException +import six + +import csv + + +def slugify(value): + """Normalizes string, converts to lowercase, removes non-alpha characters, + and converts spaces to hyphens. + + Copy/Pasted from ametaireau/pelican/utils itself took from django sources. + """ + if isinstance(value, six.text_type): + import unicodedata + value = unicodedata.normalize('NFKD', value) + if six.PY2: + value = value.encode('ascii', 'ignore') + value = six.text_type(re.sub('[^\w\s-]', '', value).strip().lower()) + return re.sub('[-\s]+', '-', value) + + +class Redirect303(HTTPException, RoutingException): + + """Raise if the map requests a redirect. This is for example the case if + `strict_slashes` are activated and an url that requires a trailing slash. + + The attribute `new_url` contains the absolute destination url. + """ + code = 303 + + def __init__(self, new_url): + RoutingException.__init__(self, new_url) + self.new_url = new_url + + def get_response(self, environ): + return redirect(self.new_url, 303) + + +class PrefixedWSGI(object): + + ''' + Wrap the application in this middleware and configure the + front-end server to add these headers, to let you quietly bind + this to a URL other than / and to an HTTP scheme that is + different than what is used locally. + + It relies on "APPLICATION_ROOT" app setting. + + Inspired from http://flask.pocoo.org/snippets/35/ + + :param app: the WSGI application + ''' + + def __init__(self, app): + self.app = app + self.wsgi_app = app.wsgi_app + + def __call__(self, environ, start_response): + script_name = self.app.config['APPLICATION_ROOT'] + if script_name: + environ['SCRIPT_NAME'] = script_name + path_info = environ['PATH_INFO'] + if path_info.startswith(script_name): + environ['PATH_INFO'] = path_info[len(script_name):] + + scheme = environ.get('HTTP_X_SCHEME', '') + if scheme: + environ['wsgi.url_scheme'] = scheme + return self.wsgi_app(environ, start_response) + + +def minimal_round(*args, **kw): + """ Jinja2 filter: rounds, but display only non-zero decimals + + from http://stackoverflow.com/questions/28458524/ + """ + # Use the original round filter, to deal with the extra arguments + res = filters.do_round(*args, **kw) + # Test if the result is equivalent to an integer and + # return depending on it + ires = int(res) + return (res if res != ires else ires) + + +def list_of_dicts2json(dict_to_convert): + """Take a list of dictionnaries and turns it into + a json in-memory file + """ + return BytesIO(dumps(dict_to_convert).encode('utf-8')) + + +def list_of_dicts2csv(dict_to_convert): + """Take a list of dictionnaries and turns it into + a csv in-memory file, assume all dict have the same keys + """ + # CSV writer has a different behavior in PY2 and PY3 + # http://stackoverflow.com/a/37974772 + try: + if six.PY3: + csv_file = StringIO() + # using list() for py3.4 compat. Otherwise, writerows() fails + # (expecting a sequence getting a view) + csv_data = [list(dict_to_convert[0].keys())] + for dic in dict_to_convert: + csv_data.append([dic[h] for h in dict_to_convert[0].keys()]) + else: + csv_file = BytesIO() + csv_data = [] + csv_data.append([key.encode('utf-8') for key in dict_to_convert[0].keys()]) + for dic in dict_to_convert: + csv_data.append( + [dic[h].encode('utf8') + if isinstance(dic[h], unicode) else str(dic[h]).encode('utf8') # NOQA + for h in dict_to_convert[0].keys()]) + except (KeyError, IndexError): + csv_data = [] + writer = csv.writer(csv_file) + writer.writerows(csv_data) + csv_file.seek(0) + if six.PY3: + csv_file = BytesIO(csv_file.getvalue().encode('utf-8')) + return csv_file + + +# base64 encoding that works with both py2 and py3 and yield no warning +base64_encode = base64.encodestring if six.PY2 else base64.encodebytes |
