diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6fbf9bfd..ceb43ce0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,10 +3,40 @@ Changelog This document describes changes between each past release. -5.0.2 (unreleased) +5.1.2 (unreleased) ================== -- Nothing changed yet. +Changed +------- + +- Minor presentation fixes (901) + + +5.1.1 (2021-10-26) +================== + +- No actual change, fixup release because 5.1.0 did not upload to pypi + + +5.1.0 (2021-10-26) +================== + +Added +----- + +- Add the option to display a "legal link" at the bottom of pages (#883) + +New settings +------------ + +- Add `LEGAL_LINK `_ setting (#883) + +Changed +------- + +- Improve performance of balance and statistics computation (#890) +- Reduce the resolution of showcase pictures by 50% (#880) +- Improve pagination style in the list of bills (#873) 5.0.1 (2021-10-20) @@ -60,6 +90,13 @@ Added - Add translations for Greek, Esperanto, Italian, Japanese, Portuguese and Swedish - Publish an `official docker image `_ +New settings +------------ + +- Add `ENABLE_CAPTCHA `_ setting (#844) +- Use and document `SESSION_COOKIE_SECURE `_ setting (#845) +- Use and document `BABEL_DEFAULT_TIMEZONE `_ setting (#590) + Changed ------- diff --git a/Dockerfile b/Dockerfile index da235d86..fca70c20 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,9 @@ ENV DEBUG="False" \ SECRET_KEY="tralala" \ SESSION_COOKIE_SECURE="True" \ SQLALCHEMY_DATABASE_URI="sqlite:////database/ihatemoney.db" \ - SQLALCHEMY_TRACK_MODIFICATIONS="False" + SQLALCHEMY_TRACK_MODIFICATIONS="False" \ + ENABLE_CAPTCHA="False" \ + LEGAL_LINK="False" RUN mkdir -p /etc/ihatemoney &&\ pip install --no-cache-dir gunicorn pymysql; diff --git a/MANIFEST.in b/MANIFEST.in index dbb963c1..ff5fdcba 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include *.rst -recursive-include ihatemoney *.rst *.py *.yaml *.po *.mo *.html *.css *.js *.eot *.svg *.woff *.txt *.png *.ini *.cfg *.j2 *.jpg *.gif *.ico +recursive-include ihatemoney *.rst *.py *.yaml *.po *.mo *.html *.css *.js *.eot *.svg *.woff *.txt *.png *.webp *.ini *.cfg *.j2 *.jpg *.gif *.ico include LICENSE CONTRIBUTORS CHANGELOG.rst diff --git a/Makefile b/Makefile index ec19e163..e6941975 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ DOC_STAMP = $(VENV)/.doc_env_installed.stamp INSTALL_STAMP = $(VENV)/.install.stamp TEMPDIR := $(shell mktemp -d) ZOPFLIPNG := zopflipng +MAGICK_MOGRIFY := mogrify .PHONY: all all: install ## Alias for install @@ -56,8 +57,13 @@ isort: install-dev ## Run the tests release: install-dev ## Release a new version (see https://ihatemoney.readthedocs.io/en/latest/contributing.html#how-to-release) $(VENV)/bin/fullrelease +.PHONY: compress-showcase +compress-showcase: + @which $(MAGICK_MOGRIFY) >/dev/null || (echo "ImageMagick 'mogrify' ($(MAGICK_MOGRIFY)) is missing" && exit 1) + $(MAGICK_MOGRIFY) -format webp -resize '75%>' -quality 50 -define webp:method=6:auto-filter=true -path ihatemoney/static/showcase/ 'assets/showcase/*.jpg' + .PHONY: compress-assets -compress-assets: ## Compress static assets +compress-assets: compress-showcase ## Compress static assets @which $(ZOPFLIPNG) >/dev/null || (echo "ZopfliPNG ($(ZOPFLIPNG)) is missing" && exit 1) mkdir $(TEMPDIR)/zopfli $(eval CPUCOUNT := $(shell python -c "import psutil; print(psutil.cpu_count(logical=False))")) diff --git a/README.rst b/README.rst index e3e20e4e..e1c63a01 100644 --- a/README.rst +++ b/README.rst @@ -13,11 +13,16 @@ I hate money :target: https://liberapay.com/IHateMoney/donate :alt: Donate +.. image:: https://img.shields.io/badge/-Docker%20image-black?logo=docker + :target: https://hub.docker.com/r/ihatemoney/ihatemoney/general + :alt: Docker image + + *I hate money* is a web application made to ease shared budget management. It keeps track of who bought what, when, and for whom; and helps to settle the bills. -* `Online documentation `_ +* `Online documentation `_ * `Hosted version `_ * `Mailing list `_ (to get updates when needed). diff --git a/ihatemoney/static/showcase/1.jpg b/assets/showcase/1.jpg similarity index 69% rename from ihatemoney/static/showcase/1.jpg rename to assets/showcase/1.jpg index a179e3a3..f0206c73 100644 Binary files a/ihatemoney/static/showcase/1.jpg and b/assets/showcase/1.jpg differ diff --git a/assets/showcase/2.jpg b/assets/showcase/2.jpg new file mode 100644 index 00000000..5d0b7108 Binary files /dev/null and b/assets/showcase/2.jpg differ diff --git a/ihatemoney/static/showcase/3.jpg b/assets/showcase/3.jpg similarity index 64% rename from ihatemoney/static/showcase/3.jpg rename to assets/showcase/3.jpg index e63e4f81..33d6328e 100644 Binary files a/ihatemoney/static/showcase/3.jpg and b/assets/showcase/3.jpg differ diff --git a/assets/showcase/4.jpg b/assets/showcase/4.jpg new file mode 100644 index 00000000..3476a2c2 Binary files /dev/null and b/assets/showcase/4.jpg differ diff --git a/ihatemoney/static/showcase/5.jpg b/assets/showcase/5.jpg similarity index 64% rename from ihatemoney/static/showcase/5.jpg rename to assets/showcase/5.jpg index 1d5ac0aa..6f8631f6 100644 Binary files a/ihatemoney/static/showcase/5.jpg and b/assets/showcase/5.jpg differ diff --git a/assets/showcase/6.jpg b/assets/showcase/6.jpg new file mode 100644 index 00000000..828d9cbd Binary files /dev/null and b/assets/showcase/6.jpg differ diff --git a/ihatemoney/static/showcase/7.jpg b/assets/showcase/7.jpg similarity index 66% rename from ihatemoney/static/showcase/7.jpg rename to assets/showcase/7.jpg index fd673152..b6709837 100644 Binary files a/ihatemoney/static/showcase/7.jpg and b/assets/showcase/7.jpg differ diff --git a/assets/showcase/8.jpg b/assets/showcase/8.jpg new file mode 100644 index 00000000..63293c17 Binary files /dev/null and b/assets/showcase/8.jpg differ diff --git a/ihatemoney/static/showcase/9.jpg b/assets/showcase/9.jpg similarity index 65% rename from ihatemoney/static/showcase/9.jpg rename to assets/showcase/9.jpg index f2916938..ec41b02f 100644 Binary files a/ihatemoney/static/showcase/9.jpg and b/assets/showcase/9.jpg differ diff --git a/ihatemoney/static/showcase/showcase.png b/assets/showcase/showcase.png similarity index 100% rename from ihatemoney/static/showcase/showcase.png rename to assets/showcase/showcase.png diff --git a/conf/entrypoint.sh b/conf/entrypoint.sh index 0f506bd9..be1e2806 100755 --- a/conf/entrypoint.sh +++ b/conf/entrypoint.sh @@ -22,6 +22,8 @@ SESSION_COOKIE_SECURE = $SESSION_COOKIE_SECURE SQLACHEMY_DEBUG = DEBUG SQLALCHEMY_DATABASE_URI = "$SQLALCHEMY_DATABASE_URI" SQLALCHEMY_TRACK_MODIFICATIONS = $SQLALCHEMY_TRACK_MODIFICATIONS +ENABLE_CAPTCHA = $ENABLE_CAPTCHA +LEGAL_LINK = "$LEGAL_LINK" EOF # Start gunicorn without forking diff --git a/docker-compose.yml b/docker-compose.yml index 22b54e34..0de37efc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,5 +24,7 @@ services: - SESSION_COOKIE_SECURE=True - SQLALCHEMY_DATABASE_URI=sqlite:////database/ihatemoney.db - SQLALCHEMY_TRACK_MODIFICATIONS=False + - ENABLE_CAPTCHA=False + - LEGAL_LINK= ports: - "8000:8000" diff --git a/docs/configuration.rst b/docs/configuration.rst index 140a12e6..5106282a 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -161,6 +161,15 @@ Note: this setting is actually interpreted by Flask-Babel, see the It is possible to add a simple captcha in order to filter out spammer bots on the form creation. In order to do so, you just have to set `ENABLE_CAPTCHA = True`. +`LEGAL_LINK` +------------ + +You may want to point to a special legal page, for instance to give information +about GDPR, or how you handle the data of your users. + +If you want to do this, you can use the `LEGAL_LINK` setting. Set it to the URL +you want. + Configuring emails sending -------------------------- @@ -176,4 +185,3 @@ possible to configure it to act differently, thanks to the great * **MAIL_USERNAME** : default **None** * **MAIL_PASSWORD** : default **None** * **DEFAULT_MAIL_SENDER** : default **None** - diff --git a/docs/contributing.rst b/docs/contributing.rst index 3006fa96..6f78c4ff 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -82,6 +82,13 @@ And then run the application:: cd ihatemoney python run.py +The docker way +-------------- + +If you prefer to use docker, then you can build your image with:: + + docker build -t ihatemoney . + Accessing dev server -------------------- @@ -213,13 +220,52 @@ And to produce a HTML doc in the `docs/_output` folder:: How to release? =============== -In order to prepare a new release, we are following the following steps: +Requirements +------------ + +To create a new release, make sure you fullfil all requirements: + +- Are you a maintainer of the pypi package? +- Are you sure you have no local tags? They will all be published + to the github process as part of the release process +- Make sure you have a ``~/.pypirc`` file with the following content, + replacing ``YOUR_PYPI_USERNAME`` with your real username:: + + [distutils] + index-servers = + pypi + + [pypi] + username:YOUR_PYPI_USERNAME + +Choosing a version number +------------------------- + +The project follows `semantic versioning `_. +To sum things up: + +- **if there is a breaking change since the last release:** increase the major + version number (11.X.Y -> 12.0.0). Example of breaking changes: drop support + for an old version of python, new setting without default value (requires + an admin to configure the new setting), changed URL paths, any other incompatible + change. Make sure to :ref:`document the upgrade process`. + +- **if there is a significant new feature or a new setting:** increase the minor + version number (11.4.Y -> 11.5.0). Make sure to :ref:`document any new settings`. + +- **if it's mostly bugfixes and small changes:** increase the patch version number + (11.4.8 -> 11.4.9) + +Making the release +------------------ + +In order to issue a new release, follow the following steps: - Merge remaining pull requests; +- Switch to the master branch; - Update :file:`CHANGELOG.rst` with the last changes; -- Update :file:`CONTRIBUTORS`; -- Update known good versions of dependencies in ``setup.cfg`` -- If needed, recompress assets. It requires zopflipng:: +- Update :file:`CONTRIBUTORS` (instructions inside the file); +- If needed, recompress assets. It requires zopflipng and ImageMagick `mogrify`:: make compress-assets @@ -228,12 +274,22 @@ In order to prepare a new release, we are following the following steps: make update-translations make build-translations -Once this is done, use the "release" instruction:: +- If you're not completely sure of yourself at this point, you + can optionally: create a new branch, push it, open a pull request, + check the CI result, and then merge the branch to master. + +Once this is done, make sure your local git repository is on the master branch, +and let's release!:: make release -And the new version should be published on PyPI. +This will publish the new version to `the Python Package Index `_ (PyPI) +and publish a tag in the git repository. .. note:: The above command will prompt for version number, handle :file:`CHANGELOG.rst` and :file:`setup.cfg` updates, package creation, pypi upload. It will prompt you before each step to get your consent. + +Finally, create a release on Github and copy the relevant changelog extract into it. +Unfortunately, you need to manually convert links to Markdown... +We have a `discussion to automate this step `_. diff --git a/docs/installation.rst b/docs/installation.rst index e6351a4f..bf87232d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -3,19 +3,80 @@ Installation ############ -We lack some knowledge about packaging to make Ihatemoney installable on mainstream -Linux distributions. If you want to give us a hand on the topic, please -check-out `the issue about debian packaging `_. +There are multiple ways to install «Ihatemoney» on your system : -If you are using Yunohost (a server operating system aiming to make self-hosting accessible to anyone), -you can use the `Ihatemoney package `_. +1. :ref:`Via Docker`. +2. `Via Yunohost `_ + (a server operating system aiming to make self-hosting accessible to anyone) +3. Do a :ref:`manual installation`. -Otherwise, follow these instructions to install it manually: +.. note:: We lack some knowledge about packaging to make Ihatemoney installable + on mainstream Linux distributions. If you want to give us a hand on the topic, + please check-out `the issue about debian packaging `_. + +.. _docker: + +With Docker +=========== + +Docker images are published `on the Docker hub `_. + +This is probably the simplest way to get something running. Once you have Docker installed +on your system, just issue :: + + docker run -d -p 8000:8000 ihatemoney/ihatemoney + +Ihatemoney is now available on http://localhost:8000. + +All :ref:`settings` can be passed with ``-e`` parameters +e.g. with a secure ``SECRET_KEY``, an external mail server and an +external database:: + + docker run -d -p 8000:8000 \ + -e SECRET_KEY="supersecure" \ + -e SQLALCHEMY_DATABASE_URI="mysql+pymysql://user:pass@10.42.58.250/ihm" \ + -e MAIL_SERVER=smtp.gmail.com \ + -e MAIL_PORT=465 \ + -e MAIL_USERNAME=your-email@gmail.com \ + -e MAIL_PASSWORD=your-password \ + -e MAIL_USE_SSL=True \ + ihatemoney/ihatemoney + +.. note: Connecting to a postgresql database is not supported for now in the + docker container. + +A volume can also be specified to persist the default database file:: + + docker run -d -p 8000:8000 -v /host/path/to/database:/database ihatemoney/ihatemoney + +To enable the Admin dashboard, first generate a hashed password with:: + + docker run -it --rm --entrypoint ihatemoney ihatemoney/ihatemoney generate_password_hash + +At the prompt, enter a password to use for the admin dashboard. The +command will print the hashed password string. + +Add these additional environment variables to the docker run invocation:: + + -e ACTIVATE_ADMIN_DASHBOARD=True \ + -e ADMIN_PASSWORD= \ + +Additional gunicorn parameters can be passed using the docker ``CMD`` +parameter. +For example, use the following command to add more gunicorn workers:: + + docker run -d -p 8000:8000 ihatemoney/ihatemoney -w 3 + + +.. _manual-installation: + +Manual Installation +=================== .. _installation-requirements: Requirements -============ +------------ «Ihatemoney» depends on: @@ -23,18 +84,19 @@ Requirements * **A Backend**: to choose among SQLite, PostgreSQL, MariaDB (>= 10.3.2) or Memory. * **Virtual environment** (recommended): `python3-venv` package under Debian/Ubuntu. -We recommend to use `virtual environment `_ but -it will work without if you prefer. +We recommend using `virtual environments `_ +to isolate the installation from other softwares on your machine, but it's not mandatory. If wondering about the backend, SQLite is the simplest and will work fine for most small to medium setups. -.. note:: If curious, source config templates can be found in the `project git repository `_. +.. note:: If curious, source config templates can be found in the + `project git repository `_. .. _virtualenv-preparation: Prepare virtual environment (recommended) -========================================= +----------------------------------------- Choose an installation path, here the current user's home directory (`~`). @@ -51,18 +113,14 @@ Activate the virtual environment:: terminal. Install -======= +------- Install the latest release with pip:: pip install ihatemoney -.. warning:: The current release of ihatemoney (4.1.5) does not work with SQLAlchemy 1.4. - The dependency will be fixed in the next version, but in the meantime you - can work around the issue with: ``pip install 'SQLAlchemy>=1.3,<1.4'``. - Test it -======= +------- Once installed, you can start a test server:: @@ -70,8 +128,23 @@ Once installed, you can start a test server:: And point your browser at `http://localhost:5000 `_. +Generate your configuration +--------------------------- + +1. Initialize the ihatemoney directories:: + + mkdir /etc/ihatemoney /var/lib/ihatemoney + +2. Generate settings:: + + ihatemoney generate-config ihatemoney.cfg > /etc/ihatemoney/ihatemoney.cfg + chmod 740 /etc/ihatemoney/ihatemoney.cfg + +You probably want to adjust ``/etc/ihatemoney/ihatemoney.cfg`` contents, +you may do it later, see :ref:`configuration`. + Configure database with MariaDB (optional) -================================================ +------------------------------------------ .. note:: Only required if you use MariaDB. Make sure to use MariaDB 10.3.2 or newer. @@ -86,9 +159,8 @@ Configure database with MariaDB (optional) 3. Create an empty database and a database user 4. Configure :ref:`SQLALCHEMY_DATABASE_URI ` accordingly - Configure database with PostgreSQL (optional) -============================================= +--------------------------------------------- .. note:: Only required if you use Postgresql. @@ -105,32 +177,17 @@ Configure database with PostgreSQL (optional) 3. Configure :ref:`SQLALCHEMY_DATABASE_URI ` accordingly. +Configure a reverse proxy +------------------------- -Deploy it -========= +When deploying this service in production, you want to have a reverse proxy +in front of the python application. -Now, if you want to deploy it on your own server, you have many options. -Three of them are documented at the moment. - -*Of course, if you want to contribute another configuration, feel free -to open a pull-request against this repository!* - - -Whatever your installation option is… --------------------------------------- - -1. Initialize the ihatemoney directories:: - - mkdir /etc/ihatemoney /var/lib/ihatemoney - -2. Generate settings:: - - ihatemoney generate-config ihatemoney.cfg > /etc/ihatemoney/ihatemoney.cfg - chmod 740 /etc/ihatemoney/ihatemoney.cfg - -You probably want to adjust ``/etc/ihatemoney/ihatemoney.cfg`` contents, -you may do it later, see :ref:`configuration`. +Here are documented two stacks. You can of course use another one if you want. +Don't hesitate to contribute a small tutorial here if you want. +1. Apache and `mod_wsgi` +2. Nginx, Gunicorn and Supervisord/Systemd With Apache and mod_wsgi ------------------------ @@ -206,52 +263,3 @@ Install Gunicorn:: .. [#systemd-services] ``/etc/systemd/system/ihatemoney.service`` path may change depending on your distribution. - -With Docker ------------ - -Build the image:: - - docker build -t ihatemoney . - -Start a daemonized Ihatemoney container:: - - docker run -d -p 8000:8000 ihatemoney - -Ihatemoney is now available on http://localhost:8000. - -All Ihatemoney settings can be passed with ``-e`` parameters -e.g. with a secure ``SECRET_KEY``, an external mail server and an -external database:: - - docker run -d -p 8000:8000 \ - -e SECRET_KEY="supersecure" \ - -e SQLALCHEMY_DATABASE_URI="mysql+pymysql://user:pass@172.17.0.5/ihm" \ - -e MAIL_SERVER=smtp.gmail.com \ - -e MAIL_PORT=465 \ - -e MAIL_USERNAME=your-email@gmail.com \ - -e MAIL_PASSWORD=your-password \ - -e MAIL_USE_SSL=True \ - ihatemoney - -A volume can also be specified to persist the default database file:: - - docker run -d -p 8000:8000 -v /host/path/to/database:/database ihatemoney - -To enable the Admin dashboard, first generate a hashed password with:: - - docker run -it --rm --entrypoint ihatemoney ihatemoney generate_password_hash - -At the prompt, enter a password to use for the admin dashboard. The -command will print the hashed password string. - -Add these additional environment variables to the docker run invocation:: - - -e ACTIVATE_ADMIN_DASHBOARD=True \ - -e ADMIN_PASSWORD= \ - -Additional gunicorn parameters can be passed using the docker ``CMD`` -parameter. -For example, use the following command to add more gunicorn workers:: - - docker run -d -p 8000:8000 ihatemoney -w 3 diff --git a/docs/upgrade.rst b/docs/upgrade.rst index 53185413..1ccfdbed 100644 --- a/docs/upgrade.rst +++ b/docs/upgrade.rst @@ -1,3 +1,5 @@ +.. _upgrade: + Upgrading ######### diff --git a/ihatemoney/conf-templates/ihatemoney.cfg.j2 b/ihatemoney/conf-templates/ihatemoney.cfg.j2 index 9d117c18..f33930dd 100644 --- a/ihatemoney/conf-templates/ihatemoney.cfg.j2 +++ b/ihatemoney/conf-templates/ihatemoney.cfg.j2 @@ -46,3 +46,8 @@ SESSION_COOKIE_SECURE = True # You can activate an optional CAPTCHA if you want to. It can be helpful # to filter spammer bots. # ENABLE_CAPTCHA = True + +# You may want to point to a special legal page, for instance to give information +# about GDPR, or how you handle the data of your users. +# Set this variable to the URL you want. +# LEGAL_LINK = "" diff --git a/ihatemoney/default_settings.py b/ihatemoney/default_settings.py index 3204ed3b..860e3b1e 100644 --- a/ihatemoney/default_settings.py +++ b/ihatemoney/default_settings.py @@ -33,3 +33,4 @@ SUPPORTED_LANGUAGES = [ "zh_Hans", ] ENABLE_CAPTCHA = False +LEGAL_LINK = "" diff --git a/ihatemoney/forms.py b/ihatemoney/forms.py index f48e5792..a55166e8 100644 --- a/ihatemoney/forms.py +++ b/ihatemoney/forms.py @@ -304,10 +304,10 @@ class ResetPasswordForm(FlaskForm): class BillForm(FlaskForm): - date = DateField(_("Date"), validators=[DataRequired()], default=datetime.now) + date = DateField(_("When?"), validators=[DataRequired()], default=datetime.now) what = StringField(_("What?"), validators=[DataRequired()]) - payer = SelectField(_("Payer"), validators=[DataRequired()], coerce=int) - amount = CalculatorStringField(_("Amount paid"), validators=[DataRequired()]) + payer = SelectField(_("Who paid?"), validators=[DataRequired()], coerce=int) + amount = CalculatorStringField(_("How much?"), validators=[DataRequired()]) currency_helper = CurrencyConverter() original_currency = SelectField(_("Currency"), validators=[DataRequired()]) external_link = URLField( diff --git a/ihatemoney/messages.pot b/ihatemoney/messages.pot index 8623dcad..c08d3703 100644 --- a/ihatemoney/messages.pot +++ b/ihatemoney/messages.pot @@ -742,6 +742,9 @@ msgstr "" msgid "Administation Dashboard" msgstr "" +msgid "Legal information" +msgstr "" + msgid "\"I hate money\" is free software" msgstr "" diff --git a/ihatemoney/models.py b/ihatemoney/models.py index 7ecffb4d..473e7c0b 100644 --- a/ihatemoney/models.py +++ b/ihatemoney/models.py @@ -99,27 +99,37 @@ class Project(db.Model): return [m for m in self.members if m.activated] @property - def balance(self): + def full_balance(self): + """Returns a triple of dicts: + - dict mapping each member to its balance + + - dict mapping each member to how much he/she should pay others + (i.e. how much he/she benefited from bills) + + - dict mapping each member to how much he/she should be paid by + others (i.e. how much he/she has paid for bills) + + """ balances, should_pay, should_receive = (defaultdict(int) for time in (1, 2, 3)) - # for each person - for person in self.members: - # get the list of bills he has to pay - bills = Bill.query.options(orm.subqueryload(Bill.owers)).filter( - Bill.owers.contains(person) - ) - for bill in bills.all(): - if person != bill.payer: - share = bill.pay_each() * person.weight - should_pay[person] += share - should_receive[bill.payer] += share + for bill in self.get_bills_unordered().all(): + should_receive[bill.payer.id] += bill.converted_amount + total_weight = sum(ower.weight for ower in bill.owers) + for ower in bill.owers: + should_pay[ower.id] += ( + ower.weight * bill.converted_amount / total_weight + ) for person in self.members: - balance = should_receive[person] - should_pay[person] + balance = should_receive[person.id] - should_pay[person.id] balances[person.id] = balance - return balances + return balances, should_pay, should_receive + + @property + def balance(self): + return self.full_balance[0] @property def members_stats(self): @@ -128,23 +138,13 @@ class Project(db.Model): :return: one stat dict per participant :rtype list: """ + balance, spent, paid = self.full_balance return [ { "member": member, - "paid": sum( - [ - bill.converted_amount - for bill in self.get_member_bills(member.id).all() - ] - ), - "spent": sum( - [ - bill.pay_each() * member.weight - for bill in self.get_bills_unordered().all() - if member in bill.owers - ] - ), - "balance": self.balance[member.id], + "paid": paid[member.id], + "spent": spent[member.id], + "balance": balance[member.id], } for member in self.active_members ] @@ -232,8 +232,13 @@ class Project(db.Model): def get_bills_unordered(self): """Base query for bill list""" + # The subqueryload option allows to pre-load data from the + # billowers table, which makes access to this data much faster. + # Without this option, any access to bill.owers would trigger a + # new SQL query, ruining overall performance. return ( - Bill.query.join(Person, Project) + Bill.query.options(orm.subqueryload(Bill.owers)) + .join(Person, Project) .filter(Bill.payer_id == Person.id) .filter(Person.project_id == Project.id) .filter(Project.id == self.id) @@ -572,7 +577,10 @@ class Bill(db.Model): } def pay_each_default(self, amount): - """Compute what each share has to pay""" + """Compute what each share has to pay. Warning: this is slow, if you need + to compute this for many bills, do it differently (see + balance_full function) + """ if self.owers: weights = ( db.session.query(func.sum(Person.weight)) @@ -587,6 +595,9 @@ class Bill(db.Model): return self.what def pay_each(self): + """Warning: this is slow, if you need to compute this for many bills, do + it differently (see balance_full function) + """ return self.pay_each_default(self.converted_amount) def __repr__(self): diff --git a/ihatemoney/static/css/main.css b/ihatemoney/static/css/main.css index 5b442ab5..a63391ba 100644 --- a/ihatemoney/static/css/main.css +++ b/ihatemoney/static/css/main.css @@ -69,6 +69,11 @@ body { width: 55px; } +#header .side-to-side { + /* avoid the man with his hand pointing at nothing */ + white-space: nowrap; +} + #header .shareimg { width: 80px; margin-left: 5px; diff --git a/ihatemoney/static/images/legal.svg b/ihatemoney/static/images/legal.svg new file mode 100644 index 00000000..223ba2b0 --- /dev/null +++ b/ihatemoney/static/images/legal.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ihatemoney/static/showcase/1.webp b/ihatemoney/static/showcase/1.webp new file mode 100644 index 00000000..24f5c164 Binary files /dev/null and b/ihatemoney/static/showcase/1.webp differ diff --git a/ihatemoney/static/showcase/2.jpg b/ihatemoney/static/showcase/2.jpg deleted file mode 100644 index 5cb2944a..00000000 Binary files a/ihatemoney/static/showcase/2.jpg and /dev/null differ diff --git a/ihatemoney/static/showcase/2.webp b/ihatemoney/static/showcase/2.webp new file mode 100644 index 00000000..4b95d84a Binary files /dev/null and b/ihatemoney/static/showcase/2.webp differ diff --git a/ihatemoney/static/showcase/3.webp b/ihatemoney/static/showcase/3.webp new file mode 100644 index 00000000..adcb3e22 Binary files /dev/null and b/ihatemoney/static/showcase/3.webp differ diff --git a/ihatemoney/static/showcase/4.jpg b/ihatemoney/static/showcase/4.jpg deleted file mode 100644 index 0561b3c4..00000000 Binary files a/ihatemoney/static/showcase/4.jpg and /dev/null differ diff --git a/ihatemoney/static/showcase/4.webp b/ihatemoney/static/showcase/4.webp new file mode 100644 index 00000000..2dd4b0c6 Binary files /dev/null and b/ihatemoney/static/showcase/4.webp differ diff --git a/ihatemoney/static/showcase/5.webp b/ihatemoney/static/showcase/5.webp new file mode 100644 index 00000000..8a65ab2d Binary files /dev/null and b/ihatemoney/static/showcase/5.webp differ diff --git a/ihatemoney/static/showcase/6.jpg b/ihatemoney/static/showcase/6.jpg deleted file mode 100644 index f49c8d34..00000000 Binary files a/ihatemoney/static/showcase/6.jpg and /dev/null differ diff --git a/ihatemoney/static/showcase/6.webp b/ihatemoney/static/showcase/6.webp new file mode 100644 index 00000000..34717164 Binary files /dev/null and b/ihatemoney/static/showcase/6.webp differ diff --git a/ihatemoney/static/showcase/7.webp b/ihatemoney/static/showcase/7.webp new file mode 100644 index 00000000..7cd9a81e Binary files /dev/null and b/ihatemoney/static/showcase/7.webp differ diff --git a/ihatemoney/static/showcase/8.jpg b/ihatemoney/static/showcase/8.jpg deleted file mode 100644 index 25cf4e66..00000000 Binary files a/ihatemoney/static/showcase/8.jpg and /dev/null differ diff --git a/ihatemoney/static/showcase/8.webp b/ihatemoney/static/showcase/8.webp new file mode 100644 index 00000000..6a732bb7 Binary files /dev/null and b/ihatemoney/static/showcase/8.webp differ diff --git a/ihatemoney/static/showcase/9.webp b/ihatemoney/static/showcase/9.webp new file mode 100644 index 00000000..c9a71d56 Binary files /dev/null and b/ihatemoney/static/showcase/9.webp differ diff --git a/ihatemoney/templates/home.html b/ihatemoney/templates/home.html index cad87390..de88ea73 100644 --- a/ihatemoney/templates/home.html +++ b/ihatemoney/templates/home.html @@ -10,8 +10,11 @@ {% endif %} {% if g.lang == 'fr' %} - ou Voir la BD explicative - + ou + + Voir la BD explicative + + {% endif %}
diff --git a/ihatemoney/templates/layout.html b/ihatemoney/templates/layout.html index 6867fcf2..aca60dd1 100644 --- a/ihatemoney/templates/layout.html +++ b/ihatemoney/templates/layout.html @@ -150,6 +150,11 @@ {{ static_include("images/cog.svg") | safe }} {% endif %} + {% if config.LEGAL_LINK %} + + {{ static_include("images/legal.svg") | safe }} + + {% endif %}
- +
{% if bills.pages > 1 %}
  • « {{ _("Newer bills") }}
  • @@ -98,7 +98,7 @@ {% endif %} {% if bills.total > 0 %} -
    + diff --git a/ihatemoney/templates/showcase.html b/ihatemoney/templates/showcase.html index cd9a371e..df5a8590 100644 --- a/ihatemoney/templates/showcase.html +++ b/ihatemoney/templates/showcase.html @@ -33,7 +33,7 @@