Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build_docker_image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

services:
mariadb:
image: mariadb:10.4
image: mariadb:10.5
env:
MARIADB_USER: amelie_test
MARIADB_PASSWORD: amelie_test
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ venv/
.cache/
media/
amelie.db
amelie.egg-info
build/

# backups of local settings, but not the .default settings
amelie/settings/local.py*
Expand Down
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the amelie docker image based on Debian 11 (Bullseye)
FROM debian:bullseye
# Build the amelie docker image based on Debian 12 (Bookworm)
FROM debian:bookworm

# Load some build variables from the pipeline
ARG BUILD_BRANCH=unknown
Expand All @@ -18,7 +18,7 @@ RUN echo "Updating repostitories..." && \
echo "Upgrading base debian system..." && \
apt-get upgrade -y && \
echo "Installing Amelie required packages..." && \
apt-get install -y apt-utils git net-tools python3 python3-pip mariadb-client libmariadb-dev xmlsec1 libssl-dev libldap-dev libsasl2-dev libjpeg-dev zlib1g-dev gettext locales acl && \
apt-get install -y apt-utils git net-tools python3 pkg-config default-libmysqlclient-dev python3-pip mariadb-client libmariadb-dev xmlsec1 libssl-dev libldap-dev libsasl2-dev libjpeg-dev zlib1g-dev gettext locales acl && \
echo "Enabling 'nl_NL' and 'en_US' locales..." && \
sed -i -e 's/# nl_NL.UTF-8 UTF-8/nl_NL.UTF-8 UTF-8/' /etc/locale.gen && \
sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
Expand All @@ -27,7 +27,7 @@ RUN echo "Updating repostitories..." && \
echo "Creating directories for amelie..." && \
mkdir -p /amelie /config /static /media /photo_upload /data_exports /homedir_exports /var/log /var/run && \
echo "Installing python requirements..." && \
pip3 install . && \
pip3 install . --break-system-packages && \
echo "Adding build variable files..." && \
echo "${BUILD_BRANCH}" > /amelie/BUILD_BRANCH && \
echo "${BUILD_COMMIT}" > /amelie/BUILD_COMMIT && \
Expand Down
19 changes: 19 additions & 0 deletions amelie/calendar/migrations/0008_alter_event_participants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 5.2 on 2025-11-11 19:26

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('calendar', '0007_alter_event_entire_day'),
('members', '0016_person_unverified_picture'),
]

operations = [
migrations.AlterField(
model_name='event',
name='participants',
field=models.ManyToManyField(blank=True, through='calendar.Participation', through_fields=('event', 'person'), to='members.person', verbose_name='Participants'),
),
]
3 changes: 2 additions & 1 deletion amelie/members/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging

from datetime import date
from datetime import timezone as tz
from decimal import Decimal
from functools import lru_cache
from io import BytesIO
Expand Down Expand Up @@ -350,7 +351,7 @@ def _person_can_be_anonymized(person):
year=membership.year, next_year=membership.year + 1, type=membership.type
)))
# Date where new SEPA authorizations came into effect.
begin = datetime.datetime(2013, 10, 30, 23, 00, 00, tzinfo=timezone.utc)
begin = datetime.datetime(2013, 10, 30, 23, 00, 00, tzinfo=tz.utc)
personal_tab_credit = Transaction.objects.filter(person=person, date__gte=begin).aggregate(Sum('price'))[
'price__sum'] or Decimal('0.00')
if personal_tab_credit != 0:
Expand Down
3 changes: 2 additions & 1 deletion amelie/personal_tab/alexia.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from django.conf import settings
from django.utils import timezone
from datetime import timezone as tz


def get_alexia():
Expand Down Expand Up @@ -43,7 +44,7 @@ def parse_datetime(datetimestr):
if datetimestr[-6:] != '+00:00':
raise ValueError('Datetime "%s" is not in UTC' % datetimestr)

return datetime.datetime.strptime(datetimestr[:-6], "%Y-%m-%dT%H:%M:%S").replace(tzinfo=timezone.utc)
return datetime.datetime.strptime(datetimestr[:-6], "%Y-%m-%dT%H:%M:%S").replace(tzinfo=tz.utc)


class AlexiaConnectionError(ConnectionError):
Expand Down
4 changes: 3 additions & 1 deletion amelie/personal_tab/debt_collection.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datetime
from datetime import timezone as tz

from django.db.models import Sum, Q
from django.template.defaultfilters import date as _date
Expand All @@ -11,6 +12,7 @@
from amelie.tools.encodings import normalize_to_ascii



def authorization_contribution(person):
"""
Returns the most appropriate contribution authorization for a given person.
Expand Down Expand Up @@ -181,7 +183,7 @@ def generate_cookie_corner_instructions(end_date):
all_transactions = Transaction.objects.filter(debt_collection=None)

# Date the SEPA debt collection went into effect: 2013-10-31 00:00 CET
begin_date = datetime.datetime(2013, 10, 30, 23, 00, 00, tzinfo=timezone.utc)
begin_date = datetime.datetime(2013, 10, 30, 23, 00, 00, tzinfo=tz.utc)

all_transactions = all_transactions.filter(date__gte=begin_date, date__lt=end_date)

Expand Down
3 changes: 2 additions & 1 deletion amelie/personal_tab/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.db.models import Q
from django.db.models.aggregates import Sum
from django.utils import timezone
from datetime import timezone as tz

from amelie.members.models import Person

Expand All @@ -21,7 +22,7 @@ def _people_with_outstanding_balance():
Returns a QuerySet with all Persons having a non-zero cookie corner balance.
"""
# Date the SEPA debt collection went into effect: 2013-10-31 00:00 CET
begin = datetime.datetime(2013, 10, 30, 23, 00, 00, tzinfo=timezone.utc)
begin = datetime.datetime(2013, 10, 30, 23, 00, 00, tzinfo=tz.utc)

return Person.objects.filter(transaction__date__gte=begin).annotate(balance=Sum('transaction__price')).filter(
balance__gt=0)
Expand Down
3 changes: 2 additions & 1 deletion amelie/personal_tab/migrations/0002_auto_20190408_2228.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
from django.db import migrations, models
from django.conf import settings
from django.utils import timezone
from datetime import timezone as tz


def fill_old_rfid_created_used_dates(apps, schema_editor):
RFIDCard = apps.get_model('personal_tab', 'RFIDCard')

default_datetime = timezone.datetime(settings.DATE_OLD_RFID_CARDS.year, settings.DATE_OLD_RFID_CARDS.month,
settings.DATE_OLD_RFID_CARDS.day, 0, 0, 0, 0, timezone.utc)
settings.DATE_OLD_RFID_CARDS.day, 0, 0, 0, 0, tz.utc)
default_datetime = default_datetime - timezone.timedelta(days=1)

for card in RFIDCard.objects.all():
Expand Down
39 changes: 21 additions & 18 deletions amelie/personal_tab/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
from amelie.tools.logic import current_association_year
from amelie.tools.mixins import RequirePersonMixin, RequireBoardMixin

from datetime import timezone as tz


DATETIMEFORMAT = '%Y%m%d%H%M%S'

logger = logging.getLogger(__name__)
Expand All @@ -59,13 +62,13 @@ def _urlize(dt):
"""
Convert datetime to url format
"""
return dt.astimezone(timezone.utc).strftime(DATETIMEFORMAT)
return dt.astimezone(tz.utc).strftime(DATETIMEFORMAT)


def _parsedatetime(inputstr):
if isinstance(inputstr, int):
inputstr = str(inputstr)
return datetime.datetime.strptime(inputstr, DATETIMEFORMAT).replace(tzinfo=timezone.utc)
return datetime.datetime.strptime(inputstr, DATETIMEFORMAT).replace(tzinfo=tz.utc)


@require_lid
Expand Down Expand Up @@ -463,8 +466,8 @@ def transaction_overview(request, date_from=False, date_to=False):
if request.method == 'POST':
form = PeriodTimeForm(request.POST)
if form.is_valid():
start = form.cleaned_data['datetime_from'].astimezone(timezone.utc)
end = form.cleaned_data['datetime_to'].astimezone(timezone.utc)
start = form.cleaned_data['datetime_from'].astimezone(tz.utc)
end = form.cleaned_data['datetime_to'].astimezone(tz.utc)
return HttpResponseRedirect(reverse('personal_tab:transactions', args=[_urlize(start), _urlize(end)]))

if not date_from:
Expand Down Expand Up @@ -608,7 +611,7 @@ def dashboard(request, pk, slug):
personal_transactions = Transaction.objects.filter(person=person).order_by('-added_on')[:5]

# Date the SEPA debt collection went into effect: 2013-10-31 00:00 CET
begin = datetime.datetime(2013, 10, 30, 23, 00, 00, tzinfo=timezone.utc)
begin = datetime.datetime(2013, 10, 30, 23, 00, 00, tzinfo=tz.utc)
now = timezone.now()
today = now.astimezone(timezone.get_default_timezone()).date()

Expand Down Expand Up @@ -660,8 +663,8 @@ def person_transactions(request, pk, slug, date_from=None, date_to=None):
if request.method == 'POST':
form = PeriodTimeForm(request.POST)
if form.is_valid():
start = form.cleaned_data['datetime_from'].astimezone(timezone.utc)
end = form.cleaned_data['datetime_to'].astimezone(timezone.utc)
start = form.cleaned_data['datetime_from'].astimezone(tz.utc)
end = form.cleaned_data['datetime_to'].astimezone(tz.utc)
return HttpResponseRedirect(reverse('personal_tab:person_transactions', kwargs={
'pk': pk, 'slug': slug, 'date_from': _urlize(start), 'date_to': _urlize(end)
}))
Expand Down Expand Up @@ -727,8 +730,8 @@ def exam_cookie_credit(request, date_from=False, date_to=False):
if request.method == 'POST':
form = PeriodTimeForm(request.POST)
if form.is_valid():
start = form.cleaned_data['datetime_from'].astimezone(timezone.utc)
end = form.cleaned_data['datetime_to'].astimezone(timezone.utc)
start = form.cleaned_data['datetime_from'].astimezone(tz.utc)
end = form.cleaned_data['datetime_to'].astimezone(tz.utc)
return HttpResponseRedirect(reverse('personal_tab:exam_cookie_credit',
args=[_urlize(start), _urlize(end)]))

Expand Down Expand Up @@ -757,8 +760,8 @@ def person_exam_cookie_credit(request, person_id, slug, date_from=None, date_to=
if request.method == 'POST':
form = PeriodTimeForm(request.POST)
if form.is_valid():
start = form.cleaned_data['datetime_from'].astimezone(timezone.utc)
end = form.cleaned_data['datetime_to'].astimezone(timezone.utc)
start = form.cleaned_data['datetime_from'].astimezone(tz.utc)
end = form.cleaned_data['datetime_to'].astimezone(tz.utc)
return HttpResponseRedirect(reverse('personal_tab:person_exam_cookie_credit', kwargs={
'person_id': person_id, 'slug': slug, 'date_from': _urlize(start), 'date_to': _urlize(end)
}))
Expand Down Expand Up @@ -803,8 +806,8 @@ def statistics_form(request):
if request.method == 'POST':
form = StatisticsForm(data=request.POST)
if form.is_valid():
start = form.cleaned_data['start_date'].astimezone(timezone.utc)
end = form.cleaned_data['end_date'].astimezone(timezone.utc)
start = form.cleaned_data['start_date'].astimezone(tz.utc)
end = form.cleaned_data['end_date'].astimezone(tz.utc)
return HttpResponseRedirect(reverse('personal_tab:statistics', kwargs={
'date_from': _urlize(start),
'date_to': _urlize(end),
Expand Down Expand Up @@ -868,7 +871,7 @@ def balance(request, dt_str=False):
if request.method == 'POST':
form = DateTimeForm(request.POST)
if form.is_valid():
dt = form.cleaned_data['datetime'].astimezone(timezone.utc)
dt = form.cleaned_data['datetime'].astimezone(tz.utc)
return HttpResponseRedirect(reverse('personal_tab:balance', args=[_urlize(dt)]))

# Redirect to form if no date given
Expand All @@ -888,7 +891,7 @@ def balance(request, dt_str=False):
dt_url = _urlize(dt)

# Date the SEPA debt collection went into effect: 2013-10-31 00:00 CET
begin = datetime.datetime(2013, 10, 30, 23, 00, 00, tzinfo=timezone.utc)
begin = datetime.datetime(2013, 10, 30, 23, 00, 00, tzinfo=tz.utc)

all_transactions = Transaction.objects.filter(date__gte=begin, date__lt=dt)
all_transactions_aggregated = all_transactions.aggregate(Sum('price'))
Expand Down Expand Up @@ -942,8 +945,8 @@ def export(request, date_from=False, date_to=False):
if request.method == 'POST':
form = PeriodTimeForm(request.POST)
if form.is_valid():
start = form.cleaned_data['datetime_from'].astimezone(timezone.utc)
end = form.cleaned_data['datetime_to'].astimezone(timezone.utc)
start = form.cleaned_data['datetime_from'].astimezone(tz.utc)
end = form.cleaned_data['datetime_to'].astimezone(tz.utc)
return HttpResponseRedirect(reverse('personal_tab:export', args=[_urlize(start), _urlize(end)]))

# Redirect to form if no date given
Expand Down Expand Up @@ -1253,7 +1256,7 @@ def debt_collection_new(request):
if request.method == 'POST':
form = DebtCollectionForm(minimal_execution_date, request.POST)
if form.is_valid():
end = form.cleaned_data['end'].astimezone(timezone.utc)
end = form.cleaned_data['end'].astimezone(tz.utc)
contribution = form.cleaned_data['contribution']
cookie_corner = form.cleaned_data['cookie_corner']

Expand Down
5 changes: 4 additions & 1 deletion amelie/settings/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@
}
]

# Override the default form rendering class to include our IA styling templates.
FORM_RENDERER = "amelie.style.forms.AmelieFormRenderer"

# Middleware classes that are used by the application
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
Expand Down Expand Up @@ -995,4 +998,4 @@

# Set language cookie settings for /graphql language switcher
LANGUAGE_COOKIE_SAMESITE = "None"
LANGUAGE_COOKIE_SECURE = "True" # Cookie is only sent over HTTPS
LANGUAGE_COOKIE_SECURE = "True" # Cookie is only sent over HTTPS
59 changes: 13 additions & 46 deletions amelie/style/forms.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,21 @@
from django.forms.forms import BaseForm
from django.forms.utils import ErrorList
from django.forms.renderers import DjangoTemplates


def as_div(self):
"""Render a form as a div with the correct CSS class"""
class AmelieFormRenderer(DjangoTemplates):
form_template_name = "style/forms/div.html"
field_template_name = "style/forms/field.html"

return self._html_output(
normal_row='<div class="form-row"%(html_class_attr)s>%(label)s %(field)s %(help_text)s</div>',
error_row='<div class="form-row"><label></label>%s</div>',
row_ender='</div>',
help_text_html=' <span class="note">%s</span>',
errors_on_separate_row=True)


class DivErrorList(ErrorList):
"""Render errors in a div with the correct css class"""

def as_divs(self):
if not self:
return ''

return '<p class="icon neg">%s</p>' % ''.join(['%s' % e for e in self])

def __str__(self):
return self.as_divs()


def update_init(old_init):
"""Override the __init__ so that the correct error_class is used"""

def _update_init(self, *args, **kwargs):
kwargs_new = {'error_class': DivErrorList}
kwargs_new.update(kwargs)
old_init(self, *args, **kwargs_new)

return _update_init
def render(self, template_name, context, request = None):
# Override the default template used to render errors. This template is determined by a different class,
# so we intercept the call in the render function itself.
if template_name == "django/forms/errors/list/default.html":
template_name = "style/forms/errors.html"
return super().render(template_name=template_name, context=context, request=request)


def inject_style(*args):
"""
Override methods and add new methods so that the new IA style is used everywhere in Forms,
without having to change it everywhere.
Empty method. Was previously used to inject our styling into forms but is unused now.
Stubbed out here because it's a pain to find and remove all usages.
"""

for form in args:
# Type checking
if not issubclass(form, BaseForm):
raise Exception("%s is not an instance of BaseForm" % form.__name__)

# Inject
form.as_div = as_div
form.__init__ = update_init(form.__init__)
form.__str__ = as_div
return
11 changes: 11 additions & 0 deletions amelie/style/static/css/compiled.css
Original file line number Diff line number Diff line change
Expand Up @@ -11259,6 +11259,11 @@ img.profile_picture {
margin-top: 0;
width: 368px;
}
form.big .tabbed-content textarea,
form.big .tabbed-content input,
form.big .tabbed-content select {
max-width: 100%;
}
.block {
background: #FFFFFF;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
Expand Down Expand Up @@ -11625,12 +11630,18 @@ form.big select {
padding: 4px 6px;
border: 1px solid #aaa;
}
form.big select {
max-width: calc(100% - 190px);
}
form.big textarea {
width: 100%;
}
form.big .form-row {
padding: 5px 0px;
}
form.big .form-row.errors {
color: #B8231F;
}
form.big .form-row label {
margin-top: 3px;
width: 180px;
Expand Down
Loading