Skip to content

Commit

Permalink
Fixes regression in meeting form.
Browse files Browse the repository at this point in the history
Actually only select guests in meeting form, as per customer
requirement.
  • Loading branch information
cyrillkuettel committed Aug 31, 2024
1 parent 4eea7d2 commit 17c83c0
Show file tree
Hide file tree
Showing 12 changed files with 114 additions and 233 deletions.
75 changes: 48 additions & 27 deletions src/privatim/forms/meeting_form.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from uuid import UUID

from sqlalchemy import select
from sqlalchemy.orm import load_only
from wtforms import StringField, validators
Expand Down Expand Up @@ -74,12 +76,25 @@ def __init__(
'request': request
}
)

query = select(User).options(
load_only(User.id, User.first_name, User.last_name)
group_id_condition = (
UUID(context.working_group.id)
if isinstance(context, Meeting)
else UUID(context.id)
)
guest_users = (
session.execute(
select(User)
.options(load_only(User.id, User.first_name, User.last_name))
.filter(
~User.groups.any(WorkingGroup.id == group_id_condition)
)
)
.scalars()
.all()
)
users = session.execute(query).scalars().all()
self.attendees.choices = [(str(u.id), u.fullname) for u in users]
self.attendees.choices = [
(str(u.id), f"{u.first_name} {u.last_name}") for u in guest_users
]

name: ConstantTextAreaField = ConstantTextAreaField(
label=_('Name'), validators=[InputRequired()]
Expand All @@ -92,8 +107,8 @@ def __init__(
)

attendees: SearchableMultiSelectField = SearchableMultiSelectField(
label=_('Members'),
validators=[InputRequired()],
label=_('Guests (Members are added by default)'),
validators=[validators.Optional()],
)

attendance = FieldList(
Expand Down Expand Up @@ -123,7 +138,7 @@ def populate_obj(self, obj: Meeting) -> None: # type:ignore[override]
self.meta.dbsession,
)
elif name == 'attendance':
# this is already handled in SearchableMultiSelectField above
# this is already handled in sync_meeting_attendance_records
pass
else:
field.populate_obj(obj, name)
Expand All @@ -137,20 +152,19 @@ def process(
**kwargs: Any
) -> None:
super().process(formdata, obj, **kwargs)
if isinstance(obj, Meeting):
self.handle_process_edit(obj)
else:
if obj is not None:
if obj is None:
return
if not formdata:
if isinstance(obj, Meeting):
self.handle_process_edit(obj)
else:
self.handle_process_add(obj) # type:ignore[arg-type]

def handle_process_edit(self, obj: Meeting) -> None:
if obj and hasattr(obj, 'attendees'):
self.attendees.data = [str(user.id) for user in obj.attendees]

self.attendance.entries = []
records = obj.sorted_attendance_records
for attendance_record in (
self.meta.dbsession.execute(records).unique().scalars().all()
self.meta.dbsession.execute(records).unique().scalars().all()
):
status = (True
if attendance_record.status == AttendanceStatus.ATTENDED
Expand Down Expand Up @@ -182,7 +196,7 @@ def handle_process_add(self, obj: WorkingGroup) -> None:


def sync_meeting_attendance_records(
meeting_form: MeetingForm,
form: MeetingForm,
obj: Meeting,
post_data: 'GetDict',
session: 'Session',
Expand All @@ -191,7 +205,7 @@ def sync_meeting_attendance_records(
for each user and map it to MeetingUserAttendance."""

def find_attendance_in_form(user_id: str) -> bool:
for f in meeting_form._fields.get('attendance', ()): # type:ignore
for f in form._fields.get('attendance', ()): # type:ignore
if f.user_id.data == user_id:
# XXX this is kind of crude, but I couldn't find a way to do
# it otherwise. Request.POST is somehow is not mapped to the
Expand All @@ -201,16 +215,23 @@ def find_attendance_in_form(user_id: str) -> bool:

return False

assert isinstance(meeting_form.attendees, SearchableMultiSelectField)
stmt = select(User).where(User.id.in_(
# FIXME: Does this give the correct result for an empty selection?
meeting_form.attendees.raw_data or ()
))
users = session.execute(stmt).scalars().all()
# Clear existing attendance records
assert isinstance(form.attendees, SearchableMultiSelectField)

# Extract user IDs and statuses from the attendance FieldList
attendance_data = {
entry.data['user_id']: entry.data['status']
for entry in form.attendance.entries
}
# Get user IDs from the attendees field
attendee_ids = set(form.attendees.data or [])

# Combine user IDs from attendance and attendees, removing duplicates
all_user_ids = set(attendance_data.keys()) | attendee_ids

stmt = select(User).where(User.id.in_(all_user_ids))
users_for_edited_meeting = session.execute(stmt).scalars().all()
obj.attendance_records = []
# Create new attendance records
for user in users:
for user in users_for_edited_meeting:
actual_status = AttendanceStatus.INVITED
if find_attendance_in_form(user.id) is True:
actual_status = AttendanceStatus.ATTENDED
Expand Down
Binary file modified src/privatim/locale/de/LC_MESSAGES/privatim.mo
Binary file not shown.
30 changes: 15 additions & 15 deletions src/privatim/locale/de/LC_MESSAGES/privatim.po
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2024-08-29 21:48+0200\n"
"POT-Creation-Date: 2024-08-31 19:01+0200\n"
"PO-Revision-Date: 2024-05-21 21:20+0200\n"
"Last-Translator: cyrill <[email protected]>\n"
"Language-Team: German <[email protected]>\n"
Expand Down Expand Up @@ -187,8 +187,7 @@ msgstr "Traktandum bearbeiten"
msgid "Delete meeting"
msgstr "Sitzung löschen"

#: src/privatim/views/meetings.py src/privatim/forms/meeting_form.py
#: src/privatim/forms/working_group_forms.py
#: src/privatim/views/meetings.py src/privatim/forms/working_group_forms.py
msgid "Members"
msgstr "Mitglieder"

Expand Down Expand Up @@ -830,10 +829,6 @@ msgstr "Beschluss"
msgid "Cantons"
msgstr "Kantone"

#: src/privatim/forms/consultation_form.py
msgid "Choose a canton..."
msgstr "Kanton auswählen..."

#: src/privatim/forms/add_comment.py src/privatim/forms/filter_form.py
msgid "Comment"
msgstr "Kommentar"
Expand Down Expand Up @@ -894,6 +889,10 @@ msgstr "Sitzung bearbeiten"
msgid "Time"
msgstr "Datum / Uhrzeit"

#: src/privatim/forms/meeting_form.py
msgid "Guests (Members are added by default)"
msgstr "Gäste (Mitglieder werden standardmässig hinzugefügt)"

#: src/privatim/forms/meeting_form.py
msgid "Attendance"
msgstr "Anwesenheit"
Expand Down Expand Up @@ -1010,14 +1009,6 @@ msgstr "Ungültige ${number_type}"
msgid "Select..."
msgstr "Auswählen"

#: src/privatim/forms/fields/fields.py
msgid "No Users Found"
msgstr "Keine Benutzer gefunden"

#: src/privatim/forms/fields/fields.py
msgid "Remove this item"
msgstr "Dieses Element entfernen"

#: src/privatim/reporting/report.py
#, python-format
msgid "Protocol of meeting ${title}"
Expand All @@ -1031,6 +1022,15 @@ msgstr "Gremium:"
msgid "Attendees:"
msgstr "Teilnehmende:"

#~ msgid "Choose a canton..."
#~ msgstr "Kanton auswählen..."

#~ msgid "No Users Found"
#~ msgstr "Keine Benutzer gefunden"

#~ msgid "Remove this item"
#~ msgstr "Dieses Element entfernen"

#, python-format
#~ msgid "Successfully deleted user: ${full_name}."
#~ msgstr "Benutzer ${full_name} erfolgreich gelöscht."
Expand Down
Binary file modified src/privatim/locale/fr/LC_MESSAGES/privatim.mo
Binary file not shown.
30 changes: 15 additions & 15 deletions src/privatim/locale/fr/LC_MESSAGES/privatim.po
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2024-08-29 21:48+0200\n"
"POT-Creation-Date: 2024-08-31 19:01+0200\n"
"PO-Revision-Date: 2024-04-11 15:53+0200\n"
"Last-Translator: cyrill <[email protected]>\n"
"Language-Team: French <[email protected]>\n"
Expand Down Expand Up @@ -184,8 +184,7 @@ msgstr "Traiter un point de l'ordre du jour"
msgid "Delete meeting"
msgstr "Supprimer la réunion"

#: src/privatim/views/meetings.py src/privatim/forms/meeting_form.py
#: src/privatim/forms/working_group_forms.py
#: src/privatim/views/meetings.py src/privatim/forms/working_group_forms.py
msgid "Members"
msgstr "Membres"

Expand Down Expand Up @@ -828,10 +827,6 @@ msgstr "Décision"
msgid "Cantons"
msgstr "Les cantons"

#: src/privatim/forms/consultation_form.py
msgid "Choose a canton..."
msgstr "Choisissez un canton..."

#: src/privatim/forms/add_comment.py src/privatim/forms/filter_form.py
msgid "Comment"
msgstr "Commentaire"
Expand Down Expand Up @@ -892,6 +887,10 @@ msgstr "Modifier la réunion"
msgid "Time"
msgstr "Date / heure"

#: src/privatim/forms/meeting_form.py
msgid "Guests (Members are added by default)"
msgstr "Invités (Les membres sont ajoutés par défaut)"

#: src/privatim/forms/meeting_form.py
msgid "Attendance"
msgstr "Participation"
Expand Down Expand Up @@ -1006,14 +1005,6 @@ msgstr "Format du ${number_type} invalide"
msgid "Select..."
msgstr "Sélectionner..."

#: src/privatim/forms/fields/fields.py
msgid "No Users Found"
msgstr "Aucun utilisateur trouvé"

#: src/privatim/forms/fields/fields.py
msgid "Remove this item"
msgstr "Supprimer cet élément"

#: src/privatim/reporting/report.py
#, python-format
msgid "Protocol of meeting ${title}"
Expand All @@ -1027,6 +1018,15 @@ msgstr "Comité:"
msgid "Attendees:"
msgstr "Participants:"

#~ msgid "Choose a canton..."
#~ msgstr "Choisissez un canton..."

#~ msgid "No Users Found"
#~ msgstr "Aucun utilisateur trouvé"

#~ msgid "Remove this item"
#~ msgstr "Supprimer cet élément"

#, python-format
#~ msgid "Successfully deleted user: ${full_name}."
#~ msgstr "Utilisateur supprimé avec succès: ${full_name}."
Expand Down
21 changes: 6 additions & 15 deletions src/privatim/locale/privatim.pot
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2024-08-29 21:48+0200\n"
"POT-Creation-Date: 2024-08-31 19:01+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down Expand Up @@ -184,8 +184,7 @@ msgstr ""
msgid "Delete meeting"
msgstr ""

#: ./src/privatim/views/meetings.py ./src/privatim/forms/meeting_form.py
#: ./src/privatim/forms/working_group_forms.py
#: ./src/privatim/views/meetings.py ./src/privatim/forms/working_group_forms.py
msgid "Members"
msgstr ""

Expand Down Expand Up @@ -818,10 +817,6 @@ msgstr ""
msgid "Cantons"
msgstr ""

#: ./src/privatim/forms/consultation_form.py
msgid "Choose a canton..."
msgstr ""

#: ./src/privatim/forms/add_comment.py ./src/privatim/forms/filter_form.py
msgid "Comment"
msgstr ""
Expand Down Expand Up @@ -880,6 +875,10 @@ msgstr ""
msgid "Time"
msgstr ""

#: ./src/privatim/forms/meeting_form.py
msgid "Guests (Members are added by default)"
msgstr ""

#: ./src/privatim/forms/meeting_form.py
msgid "Attendance"
msgstr ""
Expand Down Expand Up @@ -992,14 +991,6 @@ msgstr ""
msgid "Select..."
msgstr ""

#: ./src/privatim/forms/fields/fields.py
msgid "No Users Found"
msgstr ""

#: ./src/privatim/forms/fields/fields.py
msgid "Remove this item"
msgstr ""

#: ./src/privatim/reporting/report.py
#, python-format
msgid "Protocol of meeting ${title}"
Expand Down
16 changes: 10 additions & 6 deletions src/privatim/views/meetings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@
HTTPBadRequest,
HTTPMethodNotAllowed,
)
from sqlalchemy import select

from privatim.utils import fix_utc_to_local_time
from privatim.forms.meeting_form import (
MeetingForm,
sync_meeting_attendance_records,
)
from privatim.models import Meeting, User, WorkingGroup
from privatim.models import Meeting, WorkingGroup
from privatim.i18n import _
from privatim.i18n import translate

Expand Down Expand Up @@ -316,19 +314,25 @@ def add_meeting_view(

target_url = request.route_url('meetings', id=context.id)
if request.method == 'POST' and form.validate():
stmt = select(User).where(User.id.in_(form.attendees.raw_data or ()))
attendees = list(session.execute(stmt).scalars().all())
assert form.name.data
assert form.time.data
time = fix_utc_to_local_time(form.time.data)

meeting = Meeting(
name=form.name.data,
time=time,
attendees=attendees,
attendees=[], # sync_meeting_attendance_records will handle this
working_group=context,
creator=request.user
)
# Manually setting this. The working_group.users should always be
# part people who attend the meeting.
# The form is used to select guests on the frontend
attendees_set = set(form.attendees.data or [])
context_users_set = {str(user.id) for user in context.users}
form.attendees.data = list(attendees_set | context_users_set)
sync_meeting_attendance_records(form, meeting, request.POST, session)

session.add(meeting)
session.flush()
message = _(
Expand Down
3 changes: 1 addition & 2 deletions src/subscribers/csp_header.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ def default_csp_directives(request: 'IRequest') -> dict[str, str]:
"frame-ancestors": "'none'",
"img-src": "'self' data: blob:",
"object-src": "'self'",
# enable one inline script by hash TomSelectWidget
"script-src": "'self' blob: resource: ",
"script-src": "'self' blob: resource:",
"style-src": "'self' 'unsafe-inline'",
}

Expand Down
Loading

0 comments on commit 17c83c0

Please sign in to comment.