This commit is contained in:
Sylvieox 2024-09-20 11:59:13 +02:00 committed by GitHub
commit bdf16cafcc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 99 additions and 16 deletions

View file

@ -33,7 +33,7 @@ services:
- SECRET_KEY=tralala
- SESSION_COOKIE_SECURE=True
- SHOW_ADMIN_EMAIL=True
- SQLALCHEMY_DATABASE_URI=sqlite:////database/ihatemoney.db
- SQLALCHEMY_DATABASE_URI=sqlite:///database/ihatemoney.db
- SQLALCHEMY_TRACK_MODIFICATIONS=False
- APPLICATION_ROOT=/
- ENABLE_CAPTCHA=False

View file

@ -1,5 +1,4 @@
import sys
from hatchling.builders.hooks.plugin.interface import BuildHookInterface

BIN
ihatemoney.db Normal file

Binary file not shown.

View file

@ -8,7 +8,7 @@ DEBUG = False
# The database URI, reprensenting the type of database and how to connect to it.
# Enter an absolute path here.
SQLALCHEMY_DATABASE_URI = 'sqlite:////var/lib/ihatemoney/ihatemoney.sqlite'
SQLALCHEMY_DATABASE_URI = 'sqlite:///var/lib/ihatemoney/ihatemoney.sqlite'
SQLACHEMY_ECHO = DEBUG
# Will likely become the default value in flask-sqlalchemy >=3 ; could be removed

View file

@ -1,6 +1,6 @@
# Verbose and documented settings are in conf-templates/ihatemoney.cfg.j2
DEBUG = SQLACHEMY_ECHO = False
SQLALCHEMY_DATABASE_URI = "sqlite:////tmp/ihatemoney.db"
SQLALCHEMY_DATABASE_URI = "sqlite:///C:/Users/sylvie c/Documents/GitHub/ihatemoney/ihatemoney.db"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SECRET_KEY = "tralala"
MAIL_DEFAULT_SENDER = "Budget manager <admin@example.com>"

View file

@ -136,8 +136,15 @@ class EditProjectForm(FlaskForm):
description=_("Enter a new code if you want to change it"),
)
contact_email = StringField(_("Email"), validators=[DataRequired(), Email()])
# Create a checkbox in project settings to enable project history (keeps track of project transactions,
# like adding/settling bills). "Y/N" determines if transaction history is being recorded for the project.
project_history = BooleanField(_("Enable project history"))
# Create a checkbox in project settings to allow for recording source IP address from a given transaction listed
# in project history. "Y/N" determines if an IP address will be attached to a created entry in project history.
ip_recording = BooleanField(_("Use IP tracking for project history"))
currency_helper = CurrencyConverter()
default_currency = SelectField(
_("Default Currency"),

View file

@ -264,6 +264,14 @@ class Project(db.Model):
.order_by(Bill.id.desc())
)
@staticmethod
def filter_by_date(query, start, end):
if start and end:
return query.filter(Bill.date.between(start, end))
else:
return query
def get_bill_weights(self):
"""
Return all bills for this project, along with the sum of weight for each bill.
@ -285,6 +293,11 @@ class Project(db.Model):
"""Ordered version of get_bill_weights"""
return self.order_bills(self.get_bill_weights())
def get_filtered_date_bill_weights_ordered(self, start, end):
bill_weights_ordered = self.get_bill_weights_ordered()
filtered_bill_weights = self.filter_by_date(bill_weights_ordered, start,end )
return filtered_bill_weights
def get_member_bills(self, member_id):
"""Return the list of bills related to a specific member"""
return (

View file

@ -105,6 +105,15 @@
<li class="page-item {% if bills.page == bills.pages %}disabled{% endif %}"><a class="page-link" href="{{ url_for('main.list_bills', page=bills.next_num) }}">{{ _("Older bills") }} &raquo;</a></li>
</ul>
{% endif %}
<form action="{{ url_for(".list_bills") }}" method="post">
{{ csrf_form.csrf_token }}
<label for="start">Start Date:</label>
<input type="date" id="start" name="start" value="{{ start if start else '' }}">
<label for="end">End Date:</label>
<input type="date" id="end" name="end" value="{{ end if end else '' }}">
<input type="submit" value="Enter">
</form>
<span id="new-bill" class="ml-auto pb-2" {% if not g.project.members %} data-toggle="tooltip" title="{{_('You should start by adding participants')}}" {% endif %}>
<a href="{{ url_for('.add_bill') }}" class="btn btn-primary {% if not g.project.members %} disabled {% endif %}" data-toggle="modal" data-keyboard="true" data-target="#bill-form" autofocus>
<i class="icon icon-white before-text">{{ static_include("images/plus.svg") | safe }}</i>

View file

@ -0,0 +1,41 @@
import pytest
from unittest.mock import Mock
from ihatemoney.models import Project, Bill
@pytest.fixture
def test_filter_by_date(Project):
# Prepare mock data
mock_query = Mock()
start_date = '2024-01-01'
end_date = '2024-12-31'
# Mock the methods being called inside filter_by_date
Project.query.filter.return_value = Mock() # Assuming you're using SQLAlchemy's Query object
# Call the method to test
result = Project.filter_by_date(mock_query, start_date, end_date)
# Assertions
assert result == Project.query.filter.return_value # Check if the method returns the expected result
Project.query.filter.assert_called_once_with(Bill.date >= start_date,
Bill.date <= end_date) # Check if filter was called with the correct arguments
def test_get_filtered_date_bill_weights_ordered(Project):
# Prepare mock data
start_date = '2024-01-01'
end_date = '2024-12-31'
Project.get_bill_weights_ordered.return_value = Mock()
Project.filter_by_date.return_value = Mock()
# Call the method to test
result = Project.get_filtered_date_bill_weights_ordered(start_date, end_date)
# Assertions
assert result == Project.filter_by_date.return_value # Check if the method returns the expected result
Project.filter_by_date.assert_called_once_with(
Project.get_bill_weights_ordered.return_value, start_date,
end_date) # Check if filter_by_date was called with the correct arguments

View file

@ -130,8 +130,8 @@ def set_show_admin_dashboard_link(endpoint, values):
"""
g.show_admin_dashboard_link = (
current_app.config["ACTIVATE_ADMIN_DASHBOARD"]
and current_app.config["ADMIN_PASSWORD"]
current_app.config["ACTIVATE_ADMIN_DASHBOARD"]
and current_app.config["ADMIN_PASSWORD"]
)
g.logout_form = LogoutForm()
@ -199,7 +199,7 @@ def admin():
if request.method == "POST" and form.validate():
# Valid password
if check_password_hash(
current_app.config["ADMIN_PASSWORD"], form.admin_password.data
current_app.config["ADMIN_PASSWORD"], form.admin_password.data
):
session["is_admin"] = True
session.update()
@ -642,7 +642,7 @@ def invite():
return render_template("send_invites.html", form=form, qrcode=qrcode_svg)
@main.route("/<project_id>/")
@main.route("/<project_id>/", methods=["GET", "POST"])
def list_bills():
bill_form = get_billform_for(g.project)
# Used for CSRF validation
@ -658,18 +658,31 @@ def list_bills():
if "last_selected_payer" in session:
bill_form.payer.data = session["last_selected_payer"]
if (
"last_selected_payed_for" in session
and g.project.id in session["last_selected_payed_for"]
"last_selected_payed_for" in session
and g.project.id in session["last_selected_payed_for"]
):
bill_form.payed_for.data = session["last_selected_payed_for"][g.project.id]
# Each item will be a (weight_sum, Bill) tuple.
# TODO: improve this awkward result using column_property:
# https://docs.sqlalchemy.org/en/14/orm/mapped_sql_expr.html.
weighted_bills = g.project.get_bill_weights_ordered().paginate(
per_page=100, error_out=True
)
if request.method == "GET":
# Retrieve ordered bill weights for the project
weighted_bills = g.project.get_bill_weights_ordered().paginate(
per_page=100, error_out=True
)
elif request.method == "POST":
# Retrieve start_date and end_date from form data
start = request.form.get('start')
end = request.form.get('end')
# Retrieve filtered bill weights by date
weighted_bills = g.project.get_filtered_date_bill_weights_ordered(start, end).paginate(
per_page=100, error_out=True
)
# Render the template with the appropriate data
return render_template(
"list_bills.html",
bills=weighted_bills,
@ -678,9 +691,10 @@ def list_bills():
csrf_form=csrf_form,
add_bill=request.values.get("add_bill", False),
current_view="list_bills",
start=start if request.method == "POST" else None,
end=end if request.method == "POST" else None,
)
@main.route("/<project_id>/members/add", methods=["GET", "POST"])
def add_member():
# FIXME manage form errors on the list_bills page
@ -974,8 +988,8 @@ def feed(token):
return "", 304
if (
request.if_modified_since
and request.if_modified_since.replace(tzinfo=None) >= last_modified
request.if_modified_since
and request.if_modified_since.replace(tzinfo=None) >= last_modified
):
return "", 304