From e7d991ecc2c1aac1b8e8aa4b59ff4b85a144fae4 Mon Sep 17 00:00:00 2001 From: Youe Graillot Date: Sun, 28 Nov 2021 02:13:10 +0100 Subject: [PATCH] csv compatible import --- ihatemoney/forms.py | 3 +-- ihatemoney/utils.py | 17 ++++++++++++++++- ihatemoney/web.py | 11 +++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/ihatemoney/forms.py b/ihatemoney/forms.py index e4375b87..f8e1b528 100644 --- a/ihatemoney/forms.py +++ b/ihatemoney/forms.py @@ -187,8 +187,7 @@ class ImportProjectForm(FlaskForm): "File", validators=[ FileRequired(), - "JSON", - validators=[FileRequired(), FileAllowed(["json", "JSON"], "JSON only!")], + FileAllowed(["json", "JSON", "csv", "CSV"], "Incorrect file format"), ], description=_("Import previously exported JSON file"), ) diff --git a/ihatemoney/utils.py b/ihatemoney/utils.py index 66f5b6a4..c5e16de1 100644 --- a/ihatemoney/utils.py +++ b/ihatemoney/utils.py @@ -2,7 +2,7 @@ import ast import csv from datetime import datetime, timedelta from enum import Enum -from io import BytesIO, StringIO +from io import BytesIO, StringIO, TextIOWrapper from json import JSONEncoder, dumps import operator import os @@ -150,6 +150,21 @@ def list_of_dicts2csv(dict_to_convert): return csv_file +def csv2list_of_dicts(csv_to_convert): + """Take a csv in-memory file and turns it into + a list of dictionnaries + """ + csv_file = TextIOWrapper(csv_to_convert) + reader = csv.DictReader(csv_file) + result = [] + for r in reader: + r["amount"] = float(r["amount"]) + r["payer_weight"] = float(r["payer_weight"]) + r["owers"] = [o.strip() for o in r["owers"].split(",")] + result.append(r) + return result + + class LoginThrottler: """Simple login throttler used to limit authentication attempts based on client's ip address. When using multiple workers, remaining number of attempts can get inconsistent diff --git a/ihatemoney/web.py b/ihatemoney/web.py index de5cac61..68c41376 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -58,6 +58,7 @@ from ihatemoney.models import Bill, LoggingMode, Person, Project, db from ihatemoney.utils import ( LoginThrottler, Redirect303, + csv2list_of_dicts, format_form_errors, get_members, list_of_dicts2csv, @@ -451,6 +452,11 @@ def import_project(): data = form.file.data if data.mimetype == "application/json": json_file = json.load(data.stream) + elif data.mimetype == "text/csv": + try: + json_file = csv2list_of_dicts(data) + except Exception as e: + raise ValueError(_("Unable to parse CSV")) else: raise ValueError("Unsupported file type") @@ -554,10 +560,7 @@ def import_project(): return redirect(url_for("main.list_bills")) except ValueError as e: flash(e.args[0], category="danger") - return render_template( - "edit_project.html", - current_view="edit_project", - ) + return redirect(url_for(".edit_project")) else: for component, errors in form.errors.items(): flash(_(component + ": ") + ", ".join(errors), category="danger")