mirror of
https://github.com/spiral-project/ihatemoney.git
synced 2025-05-06 13:01:50 +02:00
Merge branch 'master' of https://github.com/spiral-project/ihatemoney into project-history
Conflicts: ihatemoney/static/css/main.css
This commit is contained in:
commit
e9831d97c4
7 changed files with 94 additions and 57 deletions
|
@ -1,14 +1,14 @@
|
||||||
Contributing
|
Contributing
|
||||||
############
|
############
|
||||||
|
|
||||||
Setup a dev environment
|
Set up a dev environment
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
You must develop on top of the git master branch::
|
You must develop on top of the Git master branch::
|
||||||
|
|
||||||
git clone https://github.com/spiral-project/ihatemoney.git
|
git clone https://github.com/spiral-project/ihatemoney.git
|
||||||
|
|
||||||
Then you need to build your dev environments. Choose your way…
|
Then you need to build your dev environment. Choose your way…
|
||||||
|
|
||||||
The quick way
|
The quick way
|
||||||
-------------
|
-------------
|
||||||
|
@ -43,7 +43,7 @@ It's as simple as that!
|
||||||
Updating
|
Updating
|
||||||
--------
|
--------
|
||||||
|
|
||||||
In case you want to update to newer versions (from git), you can just run the "update" command::
|
In case you want to update to newer versions (from Git), you can just run the "update" command::
|
||||||
|
|
||||||
make update
|
make update
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ database revision file::
|
||||||
make create-database-revision
|
make create-database-revision
|
||||||
|
|
||||||
If your changes are simple enough, the generated script will be populated with
|
If your changes are simple enough, the generated script will be populated with
|
||||||
the necessary migrations steps. You can edit the generated script. eg: to add
|
the necessary migrations steps. You can edit the generated script. e.g: To add
|
||||||
data migrations.
|
data migrations.
|
||||||
|
|
||||||
For complex migrations, it is recommended to start from an empty revision file
|
For complex migrations, it is recommended to start from an empty revision file
|
||||||
|
@ -101,7 +101,7 @@ To do so::
|
||||||
make test
|
make test
|
||||||
|
|
||||||
We are using the `black <https://black.readthedocs.io/en/stable/>`_ formatter
|
We are using the `black <https://black.readthedocs.io/en/stable/>`_ formatter
|
||||||
for all the python files in this project. Be sure to run it locally on your
|
for all the Python files in this project. Be sure to run it locally on your
|
||||||
files. To do so, just run::
|
files. To do so, just run::
|
||||||
|
|
||||||
black ihatemoney
|
black ihatemoney
|
||||||
|
@ -112,16 +112,16 @@ hook, for instance).
|
||||||
As a designer / Front-end developer
|
As a designer / Front-end developer
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
Feel free to provide us mockups or to involve yourself into the discussions
|
Feel free to provide mockups, or to involve yourself in the discussions
|
||||||
hapenning on the github issue tracker. All ideas are welcome. Of course, if you
|
happening on the GitHub issue tracker. All ideas are welcome. Of course, if you
|
||||||
know how to implement them, feel free to fork and make a pull request.
|
know how to implement them, feel free to fork and make a pull request.
|
||||||
|
|
||||||
As a translator
|
As a translator
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
If you're able to translate Ihatemoney in your own language,
|
If you're able to translate Ihatemoney in your own language,
|
||||||
head over to `the website we use for translations <https://hosted.weblate.org/settings/i-hate-money/i-hate-money/>`_
|
head over to `the website we use for translations <https://hosted.weblate.org/projects/i-hate-money/i-hate-money/>`_
|
||||||
and start translating!
|
and start translating.
|
||||||
|
|
||||||
All the heavy lifting will be done automatically, and your strings will
|
All the heavy lifting will be done automatically, and your strings will
|
||||||
eventually be integrated.
|
eventually be integrated.
|
||||||
|
@ -134,7 +134,7 @@ End-user
|
||||||
|
|
||||||
You are using the application and found a bug? You have some ideas about how to
|
You are using the application and found a bug? You have some ideas about how to
|
||||||
improve the project? Please tell us `by filling a new issue <https://github.com/spiral-project/ihatemoney/issues>`_.
|
improve the project? Please tell us `by filling a new issue <https://github.com/spiral-project/ihatemoney/issues>`_.
|
||||||
Or, if you prefer, you can send me an email to `alexis@notmyidea.org` and I
|
Or, if you prefer, you can send me an e-mail to `alexis@notmyidea.org` and I
|
||||||
will update the issue tracker with your feedback.
|
will update the issue tracker with your feedback.
|
||||||
|
|
||||||
Thanks again!
|
Thanks again!
|
||||||
|
@ -150,7 +150,7 @@ Install doc dependencies (within the virtualenv, if any)::
|
||||||
|
|
||||||
pip install -r docs/requirements.txt
|
pip install -r docs/requirements.txt
|
||||||
|
|
||||||
And to produce html doc in `docs/_output` folder::
|
And to produce a HTML doc in the `docs/_output` folder::
|
||||||
|
|
||||||
cd docs/
|
cd docs/
|
||||||
make html
|
make html
|
||||||
|
|
|
@ -127,8 +127,11 @@ class EditProjectForm(FlaskForm):
|
||||||
|
|
||||||
class UploadForm(FlaskForm):
|
class UploadForm(FlaskForm):
|
||||||
file = FileField(
|
file = FileField(
|
||||||
"JSON", validators=[FileRequired(), FileAllowed(["json", "JSON"], "JSON only!")]
|
"JSON",
|
||||||
|
validators=[FileRequired(), FileAllowed(["json", "JSON"], "JSON only!")],
|
||||||
|
description=_("Import previously exported JSON file"),
|
||||||
)
|
)
|
||||||
|
submit = SubmitField(_("Import"))
|
||||||
|
|
||||||
|
|
||||||
class ProjectForm(EditProjectForm):
|
class ProjectForm(EditProjectForm):
|
||||||
|
|
|
@ -150,6 +150,11 @@ body {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
margin-left: 25px;
|
margin-left: 25px;
|
||||||
}
|
}
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.home .card {
|
||||||
|
min-width: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* Other */
|
/* Other */
|
||||||
|
|
||||||
#bills {
|
#bills {
|
||||||
|
@ -180,7 +185,7 @@ footer {
|
||||||
padding: 45px 50px;
|
padding: 45px 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 1024px) {
|
||||||
footer {
|
footer {
|
||||||
padding-left: calc(25% + 50px);
|
padding-left: calc(25% + 50px);
|
||||||
}
|
}
|
||||||
|
@ -320,13 +325,16 @@ footer .footer-left {
|
||||||
background: url("../images/see.png") no-repeat right;
|
background: url("../images/see.png") no-repeat right;
|
||||||
}
|
}
|
||||||
|
|
||||||
#bill_table, #monthly_stats, #history_table {
|
#bill_table,
|
||||||
|
#monthly_stats,
|
||||||
|
#history_table {
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.split_bills, #table_overflow.statistics {
|
.split_bills,
|
||||||
|
#table_overflow.statistics {
|
||||||
/* The table is shifted to left, so add the spacer width on the right to match */
|
/* The table is shifted to left, so add the spacer width on the right to match */
|
||||||
width: calc(100% + 15px);
|
width: calc(100% + 15px);
|
||||||
}
|
}
|
||||||
|
@ -550,3 +558,13 @@ footer .icon svg {
|
||||||
#history_warnings {
|
#history_warnings {
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* edit settings */
|
||||||
|
|
||||||
|
.edit-project form {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 3em;
|
||||||
|
}
|
||||||
|
.edit-project .custom-file {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
|
@ -5,18 +5,43 @@
|
||||||
{
|
{
|
||||||
$(this).html("<a style='color:red; ' href='{{ url_for('.delete_project') }}' >{{_("you sure?")}}</a>");
|
$(this).html("<a style='color:red; ' href='{{ url_for('.delete_project') }}' >{{_("you sure?")}}</a>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.custom-file-input').on('change', function(event) {
|
||||||
|
var filename = [].slice.call(this.files).map(function (file) { return file.name}).join(',')
|
||||||
|
var $labelElement = $(this).parents('.custom-file').find('.custom-file-label')
|
||||||
|
$labelElement.text(filename)
|
||||||
|
})
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>{{ _("Edit project") }}</h2>
|
<div class="container edit-project">
|
||||||
<p>
|
|
||||||
<form class="form-horizontal" method="post">
|
|
||||||
{{ forms.edit_project(edit_form) }}
|
|
||||||
</form>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>{{ _("Download project's data") }}</h2>
|
<h2>{{ _("Edit project") }}</h2>
|
||||||
<p>
|
<form class="form-horizontal" method="post">
|
||||||
|
{{ forms.edit_project(edit_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>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h2>{{ _("Download project's data") }}</h2>
|
||||||
<div class="list-group download-project">
|
<div class="list-group download-project">
|
||||||
<div class="list-group-item list-group-item-action">
|
<div class="list-group-item list-group-item-action">
|
||||||
<h5 class="d-flex w-100 justify-content-between">
|
<h5 class="d-flex w-100 justify-content-between">
|
||||||
|
@ -51,5 +76,5 @@
|
||||||
<p class="mb-1 text-muted">{{ _('Download the list of transactions needed to settle the current bills.') }}</p>
|
<p class="mb-1 text-muted">{{ _('Download the list of transactions needed to settle the current bills.') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
<li class="nav-item{% if current_view == 'statistics' %} active{% endif %}"><a class="nav-link" href="{{ url_for("main.statistics") }}">{{ _("Statistics") }}</a></li>
|
<li class="nav-item{% if current_view == 'statistics' %} active{% endif %}"><a class="nav-link" href="{{ url_for("main.statistics") }}">{{ _("Statistics") }}</a></li>
|
||||||
<li class="nav-item{% if current_view == 'history' %} active{% endif %}"><a class="nav-link" href="{{ url_for("main.history") }}">{{ _("History") }}</a></li>
|
<li class="nav-item{% if current_view == 'history' %} active{% endif %}"><a class="nav-link" href="{{ url_for("main.history") }}">{{ _("History") }}</a></li>
|
||||||
<li class="nav-item{% if current_view == 'edit_project' %} active{% endif %}"><a class="nav-link" href="{{ url_for("main.edit_project") }}">{{ _("Settings") }}</a></li>
|
<li class="nav-item{% if current_view == 'edit_project' %} active{% endif %}"><a class="nav-link" href="{{ url_for("main.edit_project") }}">{{ _("Settings") }}</a></li>
|
||||||
<li class="nav-item{% if current_view == 'upload_json' %} active{% endif %}"><a class="nav-link" href="{{ url_for("main.upload_json") }}">{{ _("Import") }}</a></li>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
{% extends "layout.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h2>{{ _("Import JSON") }}</h2>
|
|
||||||
<p>
|
|
||||||
<form class="form-horizontal" method="post" enctype="multipart/form-data">
|
|
||||||
{{ forms.upload_json(form) }}
|
|
||||||
</form>
|
|
||||||
</p>
|
|
||||||
{% endblock %}
|
|
|
@ -237,6 +237,8 @@ def authenticate(project_id=None):
|
||||||
# add the project on the top of the list
|
# add the project on the top of the list
|
||||||
session["projects"].insert(0, (project_id, project.name))
|
session["projects"].insert(0, (project_id, project.name))
|
||||||
session[project_id] = True
|
session[project_id] = True
|
||||||
|
# Set session to permanent to make language choice persist
|
||||||
|
session.permanent = True
|
||||||
session.update()
|
session.update()
|
||||||
setattr(g, "project", project)
|
setattr(g, "project", project)
|
||||||
return redirect(url_for(".list_bills"))
|
return redirect(url_for(".list_bills"))
|
||||||
|
@ -384,37 +386,37 @@ def reset_password():
|
||||||
@main.route("/<project_id>/edit", methods=["GET", "POST"])
|
@main.route("/<project_id>/edit", methods=["GET", "POST"])
|
||||||
def edit_project():
|
def edit_project():
|
||||||
edit_form = EditProjectForm()
|
edit_form = EditProjectForm()
|
||||||
if request.method == "POST":
|
import_form = UploadForm()
|
||||||
if edit_form.validate():
|
# Import form
|
||||||
project = edit_form.update(g.project)
|
if import_form.validate_on_submit():
|
||||||
db.session.add(project)
|
try:
|
||||||
db.session.commit()
|
import_project(import_form.file.data.stream, g.project)
|
||||||
|
flash(_("Project successfully uploaded"))
|
||||||
|
|
||||||
return redirect(url_for(".list_bills"))
|
return redirect(url_for("main.list_bills"))
|
||||||
|
except ValueError:
|
||||||
|
flash(_("Invalid JSON"), category="error")
|
||||||
|
|
||||||
|
# Edit form
|
||||||
|
if edit_form.validate_on_submit():
|
||||||
|
project = edit_form.update(g.project)
|
||||||
|
db.session.add(project)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return redirect(url_for("main.list_bills"))
|
||||||
else:
|
else:
|
||||||
edit_form.name.data = g.project.name
|
edit_form.name.data = g.project.name
|
||||||
edit_form.logging_preferences.data = g.project.logging_preference
|
edit_form.logging_preferences.data = g.project.logging_preference
|
||||||
edit_form.contact_email.data = g.project.contact_email
|
edit_form.contact_email.data = g.project.contact_email
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"edit_project.html", edit_form=edit_form, current_view="edit_project"
|
"edit_project.html",
|
||||||
|
edit_form=edit_form,
|
||||||
|
import_form=import_form,
|
||||||
|
current_view="edit_project",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@main.route("/<project_id>/upload_json", methods=["GET", "POST"])
|
|
||||||
def upload_json():
|
|
||||||
form = UploadForm()
|
|
||||||
if form.validate_on_submit():
|
|
||||||
try:
|
|
||||||
import_project(form.file.data.stream, g.project)
|
|
||||||
flash(_("Project successfully uploaded"))
|
|
||||||
except ValueError:
|
|
||||||
flash(_("Invalid JSON"), category="error")
|
|
||||||
return redirect(url_for("main.list_bills"))
|
|
||||||
|
|
||||||
return render_template("upload_json.html", form=form)
|
|
||||||
|
|
||||||
|
|
||||||
def import_project(file, project):
|
def import_project(file, project):
|
||||||
json_file = json.load(file)
|
json_file = json.load(file)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue