mirror of
https://github.com/spiral-project/ihatemoney.git
synced 2025-05-05 12:41:49 +02:00
proper import form (fix messy errors)
This commit is contained in:
parent
d0a8668de6
commit
d24efac10a
4 changed files with 149 additions and 128 deletions
|
@ -182,13 +182,16 @@ class EditProjectForm(FlaskForm):
|
|||
return project
|
||||
|
||||
|
||||
class UploadForm(FlaskForm):
|
||||
class ImportProjectForm(FlaskForm):
|
||||
file = FileField(
|
||||
"File",
|
||||
validators=[
|
||||
FileRequired(),
|
||||
"JSON",
|
||||
validators=[FileRequired(), FileAllowed(["json", "JSON"], "JSON only!")],
|
||||
],
|
||||
description=_("Import previously exported JSON file"),
|
||||
)
|
||||
submit = SubmitField(_("Import"))
|
||||
|
||||
|
||||
class ProjectForm(EditProjectForm):
|
||||
|
@ -240,7 +243,7 @@ class ProjectFormWithCaptcha(ProjectForm):
|
|||
raise ValidationError(Markup(message))
|
||||
|
||||
|
||||
class DestructiveActionProjectForm(FlaskForm):
|
||||
class DeleteProjectForm(FlaskForm):
|
||||
"""Used for any important "delete" action linked to a project:
|
||||
|
||||
- delete project itself
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<div class="container edit-project">
|
||||
|
||||
<h2>{{ _("Edit project") }}</h2>
|
||||
<form class="form-horizontal" method="post">
|
||||
<form id="edit-project" class="form-horizontal" method="post">
|
||||
{{ forms.edit_project(edit_form) }}
|
||||
</form>
|
||||
|
||||
|
@ -38,23 +38,10 @@
|
|||
{{ forms.delete_project(delete_form) }}
|
||||
</form>
|
||||
|
||||
<h2>{{ _("Import JSON") }}</h2>
|
||||
<form class="form-horizontal" method="post" enctype="multipart/form-data">
|
||||
{{ import_form.hidden_tag() }}
|
||||
|
||||
<div class="custom-file">
|
||||
<div class="form-group">
|
||||
{{ import_form.file(class="custom-file-input") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ import_form.file.description }}
|
||||
</small>
|
||||
</div>
|
||||
<label class="custom-file-label" for="customFile">{{ _('Choose file') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
{{ import_form.submit(class="btn btn-primary") }}
|
||||
</div>
|
||||
<h2>{{ _("Import project") }}</h2>
|
||||
<form id="import-project" class="form-horizontal" action="{{ url_for(".import_project") }}" method="post" enctype="multipart/form-data">
|
||||
{{ forms.import_project(import_form) }}
|
||||
</form>
|
||||
|
||||
<h2>{{ _("Download project's data") }}</h2>
|
||||
|
|
|
@ -118,13 +118,25 @@
|
|||
|
||||
{% endmacro %}
|
||||
|
||||
{% macro upload_json(form) %}
|
||||
{% macro import_project(form) %}
|
||||
|
||||
{% include "display_errors.html" %}
|
||||
{{ form.hidden_tag() }}
|
||||
{{ form.file }}
|
||||
|
||||
<div class="custom-file">
|
||||
<div class="form-group">
|
||||
{{ form.file(class="custom-file-input") }}
|
||||
<small class="form-text text-muted">
|
||||
{{ form.file.description }}
|
||||
</small>
|
||||
</div>
|
||||
<label class="custom-file-label" for="customFile">{{ _('Choose file') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary">{{ _("Import") }}</button>
|
||||
</div>
|
||||
|
||||
{% endmacro %}
|
||||
|
||||
{% macro delete_project_history(form) %}
|
||||
|
|
|
@ -41,16 +41,16 @@ from ihatemoney.emails import send_creation_email
|
|||
from ihatemoney.forms import (
|
||||
AdminAuthenticationForm,
|
||||
AuthenticationForm,
|
||||
DestructiveActionProjectForm,
|
||||
DeleteProjectForm,
|
||||
EditProjectForm,
|
||||
EmptyForm,
|
||||
ImportProjectForm,
|
||||
InviteForm,
|
||||
MemberForm,
|
||||
PasswordReminder,
|
||||
ProjectForm,
|
||||
ProjectFormWithCaptcha,
|
||||
ResetPasswordForm,
|
||||
UploadForm,
|
||||
get_billform_for,
|
||||
)
|
||||
from ihatemoney.history import get_history, get_history_queries
|
||||
|
@ -412,17 +412,8 @@ def reset_password():
|
|||
@main.route("/<project_id>/edit", methods=["GET", "POST"])
|
||||
def edit_project():
|
||||
edit_form = EditProjectForm(id=g.project.id)
|
||||
delete_form = DestructiveActionProjectForm(id=g.project.id)
|
||||
import_form = UploadForm()
|
||||
# Import form
|
||||
if import_form.validate_on_submit():
|
||||
try:
|
||||
import_project(import_form.file.data.stream, g.project)
|
||||
flash(_("Project successfully uploaded"))
|
||||
|
||||
return redirect(url_for("main.list_bills"))
|
||||
except ValueError as e:
|
||||
flash(e.args[0], category="danger")
|
||||
import_form = ImportProjectForm(id=g.project.id)
|
||||
delete_form = DeleteProjectForm(id=g.project.id)
|
||||
|
||||
# Edit form
|
||||
if edit_form.validate_on_submit():
|
||||
|
@ -446,108 +437,136 @@ def edit_project():
|
|||
return render_template(
|
||||
"edit_project.html",
|
||||
edit_form=edit_form,
|
||||
delete_form=delete_form,
|
||||
import_form=import_form,
|
||||
delete_form=delete_form,
|
||||
current_view="edit_project",
|
||||
)
|
||||
|
||||
|
||||
def import_project(file, project):
|
||||
json_file = json.load(file)
|
||||
@main.route("/<project_id>/import", methods=["POST"])
|
||||
def import_project():
|
||||
form = ImportProjectForm()
|
||||
if form.validate():
|
||||
try:
|
||||
data = form.file.data
|
||||
if data.mimetype == "application/json":
|
||||
json_file = json.load(data.stream)
|
||||
else:
|
||||
raise ValueError("Unsupported file type")
|
||||
|
||||
# Check if JSON is correct
|
||||
attr = ["what", "payer_name", "payer_weight", "amount", "currency", "date", "owers"]
|
||||
attr.sort()
|
||||
currencies = set()
|
||||
for e in json_file:
|
||||
# If currency is absent, empty, or explicitly set to XXX
|
||||
# set it to project default.
|
||||
if e.get("currency", "") in ["", "XXX"]:
|
||||
e["currency"] = project.default_currency
|
||||
if len(e) != len(attr):
|
||||
raise ValueError(_("Invalid JSON"))
|
||||
list_attr = []
|
||||
for i in e:
|
||||
list_attr.append(i)
|
||||
list_attr.sort()
|
||||
if list_attr != attr:
|
||||
raise ValueError(_("Invalid JSON"))
|
||||
# Keep track of currencies
|
||||
currencies.add(e["currency"])
|
||||
# Check if JSON is correct
|
||||
attr = [
|
||||
"what",
|
||||
"payer_name",
|
||||
"payer_weight",
|
||||
"amount",
|
||||
"currency",
|
||||
"date",
|
||||
"owers",
|
||||
]
|
||||
attr.sort()
|
||||
currencies = set()
|
||||
for e in json_file:
|
||||
# If currency is absent, empty, or explicitly set to XXX
|
||||
# set it to project default.
|
||||
if e.get("currency", "") in ["", "XXX"]:
|
||||
e["currency"] = g.project.default_currency
|
||||
if len(e) != len(attr):
|
||||
raise ValueError(_("Invalid JSON"))
|
||||
list_attr = []
|
||||
for i in e:
|
||||
list_attr.append(i)
|
||||
list_attr.sort()
|
||||
if list_attr != attr:
|
||||
raise ValueError(_("Invalid JSON"))
|
||||
# Keep track of currencies
|
||||
currencies.add(e["currency"])
|
||||
|
||||
# Additional checks if project has no default currency
|
||||
if project.default_currency == CurrencyConverter.no_currency:
|
||||
# If bills have currencies, they must be consistent
|
||||
if len(currencies - {CurrencyConverter.no_currency}) >= 2:
|
||||
raise ValueError(
|
||||
_(
|
||||
"Cannot add bills in multiple currencies to a project without default currency"
|
||||
)
|
||||
# Additional checks if project has no default currency
|
||||
if g.project.default_currency == CurrencyConverter.no_currency:
|
||||
# If bills have currencies, they must be consistent
|
||||
if len(currencies - {CurrencyConverter.no_currency}) >= 2:
|
||||
raise ValueError(
|
||||
_(
|
||||
"Cannot add bills in multiple currencies to a project without default currency"
|
||||
)
|
||||
)
|
||||
# Strip currency from bills (since it's the same for every bill)
|
||||
for e in json_file:
|
||||
e["currency"] = CurrencyConverter.no_currency
|
||||
|
||||
# From json : export list of members
|
||||
members_json = get_members(json_file)
|
||||
members = g.project.members
|
||||
members_already_here = list()
|
||||
for m in members:
|
||||
members_already_here.append(str(m))
|
||||
|
||||
# List all members not in the project and weight associated
|
||||
# List of tuples (name,weight)
|
||||
members_to_add = list()
|
||||
for i in members_json:
|
||||
if str(i[0]) not in members_already_here:
|
||||
members_to_add.append(i)
|
||||
|
||||
# List bills not in the project
|
||||
# Same format than JSON element
|
||||
project_bills = g.project.get_pretty_bills()
|
||||
bill_to_add = list()
|
||||
for j in json_file:
|
||||
same = False
|
||||
for p in project_bills:
|
||||
if same_bill(p, j):
|
||||
same = True
|
||||
break
|
||||
if not same:
|
||||
bill_to_add.append(j)
|
||||
|
||||
# Add users to DB
|
||||
for m in members_to_add:
|
||||
Person(name=m[0], project=g.project, weight=m[1])
|
||||
db.session.commit()
|
||||
|
||||
id_dict = {}
|
||||
for i in g.project.members:
|
||||
id_dict[i.name] = i.id
|
||||
|
||||
# Create bills
|
||||
for b in bill_to_add:
|
||||
owers_id = list()
|
||||
for ower in b["owers"]:
|
||||
owers_id.append(id_dict[ower])
|
||||
|
||||
bill = Bill()
|
||||
form = get_billform_for(g.project)
|
||||
form.what = b["what"]
|
||||
form.amount = b["amount"]
|
||||
form.original_currency = b["currency"]
|
||||
form.date = parse(b["date"])
|
||||
form.payer = id_dict[b["payer_name"]]
|
||||
form.payed_for = owers_id
|
||||
|
||||
db.session.add(form.fake_form(bill, g.project))
|
||||
|
||||
# Add bills to DB
|
||||
db.session.commit()
|
||||
flash(_("Project successfully uploaded"))
|
||||
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",
|
||||
)
|
||||
# Strip currency from bills (since it's the same for every bill)
|
||||
for e in json_file:
|
||||
e["currency"] = CurrencyConverter.no_currency
|
||||
|
||||
# From json : export list of members
|
||||
members_json = get_members(json_file)
|
||||
members = project.members
|
||||
members_already_here = list()
|
||||
for m in members:
|
||||
members_already_here.append(str(m))
|
||||
|
||||
# List all members not in the project and weight associated
|
||||
# List of tuples (name,weight)
|
||||
members_to_add = list()
|
||||
for i in members_json:
|
||||
if str(i[0]) not in members_already_here:
|
||||
members_to_add.append(i)
|
||||
|
||||
# List bills not in the project
|
||||
# Same format than JSON element
|
||||
project_bills = project.get_pretty_bills()
|
||||
bill_to_add = list()
|
||||
for j in json_file:
|
||||
same = False
|
||||
for p in project_bills:
|
||||
if same_bill(p, j):
|
||||
same = True
|
||||
break
|
||||
if not same:
|
||||
bill_to_add.append(j)
|
||||
|
||||
# Add users to DB
|
||||
for m in members_to_add:
|
||||
Person(name=m[0], project=project, weight=m[1])
|
||||
db.session.commit()
|
||||
|
||||
id_dict = {}
|
||||
for i in project.members:
|
||||
id_dict[i.name] = i.id
|
||||
|
||||
# Create bills
|
||||
for b in bill_to_add:
|
||||
owers_id = list()
|
||||
for ower in b["owers"]:
|
||||
owers_id.append(id_dict[ower])
|
||||
|
||||
bill = Bill()
|
||||
form = get_billform_for(project)
|
||||
form.what = b["what"]
|
||||
form.amount = b["amount"]
|
||||
form.original_currency = b["currency"]
|
||||
form.date = parse(b["date"])
|
||||
form.payer = id_dict[b["payer_name"]]
|
||||
form.payed_for = owers_id
|
||||
|
||||
db.session.add(form.fake_form(bill, project))
|
||||
|
||||
# Add bills to DB
|
||||
db.session.commit()
|
||||
else:
|
||||
for component, errors in form.errors.items():
|
||||
flash(_(component + ": ") + ", ".join(errors), category="danger")
|
||||
return redirect(url_for(".edit_project"))
|
||||
|
||||
|
||||
@main.route("/<project_id>/delete", methods=["POST"])
|
||||
def delete_project():
|
||||
form = DestructiveActionProjectForm(id=g.project.id)
|
||||
form = DeleteProjectForm(id=g.project.id)
|
||||
if form.validate():
|
||||
g.project.remove_project()
|
||||
flash(_("Project successfully deleted"))
|
||||
|
@ -838,7 +857,7 @@ def history():
|
|||
|
||||
any_ip_addresses = any(event["ip"] for event in history)
|
||||
|
||||
delete_form = DestructiveActionProjectForm()
|
||||
delete_form = DeleteProjectForm()
|
||||
return render_template(
|
||||
"history.html",
|
||||
current_view="history",
|
||||
|
@ -854,7 +873,7 @@ def history():
|
|||
@main.route("/<project_id>/erase_history", methods=["POST"])
|
||||
def erase_history():
|
||||
"""Erase all history entries associated with this project."""
|
||||
form = DestructiveActionProjectForm(id=g.project.id)
|
||||
form = DeleteProjectForm(id=g.project.id)
|
||||
if not form.validate():
|
||||
flash(
|
||||
format_form_errors(form, _("Error deleting project history")),
|
||||
|
@ -873,7 +892,7 @@ def erase_history():
|
|||
@main.route("/<project_id>/strip_ip_addresses", methods=["POST"])
|
||||
def strip_ip_addresses():
|
||||
"""Strip ip addresses from history entries associated with this project."""
|
||||
form = DestructiveActionProjectForm(id=g.project.id)
|
||||
form = DeleteProjectForm(id=g.project.id)
|
||||
if not form.validate():
|
||||
flash(
|
||||
format_form_errors(form, _("Error deleting recorded IP addresses")),
|
||||
|
|
Loading…
Reference in a new issue