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
7 changes: 2 additions & 5 deletions src/onegov/pas/collections/attendence.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,10 @@ def for_commission_president(
(Attendence.commission_id.in_(active_commission_ids))
)

def view_for_parliamentarian(
def query_for_current_user(
self, request: PasRequest
) -> list[Attendence]:
"""
Returns filtered attendances based on user role and permissions.
This encapsulates the filtering logic previously in the view.
"""
"""Returns attendances filtered by the current user's role."""
user = request.current_user

if not request.is_parliamentarian:
Expand Down
21 changes: 21 additions & 0 deletions src/onegov/pas/layouts/attendence.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,27 @@ def breadcrumbs(self) -> list[Link]:

@cached_property
def editbar_links(self) -> list[Link] | None:
if self.model.bulk_edit_id:
name = (
'edit-plenary-bulk-attendences'
if self.model.type == 'plenary'
else 'edit-commission-bulk-attendences'
)
if self.request.is_admin or (
self.request.is_parliamentarian
and self.request.current_parliamentarian
and str(self.request.current_parliamentarian.id)
== str(self.model.parliamentarian_id)
):
return [
Link(
text=_('Edit bulk'),
url=self.request.link(self.model, name),
attrs={'class': 'edit-link'},
)
]
return None

if self.request.is_admin:
return [
Link(
Expand Down
44 changes: 37 additions & 7 deletions src/onegov/pas/locale/de_CH/LC_MESSAGES/onegov.pas.po
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,9 @@ msgstr "Massenbuchung Plenarsitzung"
msgid "Commission session (bulk)"
msgstr "Massenbuchung Kommissionssitzung"

msgid "Edit bulk"
msgstr "Massenbuchung bearbeiten"

msgid "Edit"
msgstr "Bearbeiten"

Expand Down Expand Up @@ -589,12 +592,15 @@ msgstr "Dauer"
msgid "No meeting defined yet."
msgstr "Noch keine Sitzung erfasst."

msgid "Bulk edits"
msgstr "Massenbuchungen"
msgid "Session date"
msgstr "Sitzungsdatum"

msgid "edited"
msgstr "bearbeitet"

msgid "Bulk edits"
msgstr "Massenbuchungen"

msgid "Timestamp"
msgstr "Zeitpunkt"

Expand Down Expand Up @@ -908,8 +914,8 @@ msgstr "Sitzung hinzugefügt"
msgid "You do not have permission to edit plenary sessions."
msgstr "Sie haben keine Berechtigung, Plenarsitzungen zu bearbeiten."

msgid "Edit plenary session"
msgstr "Plenarsitzung bearbeiten"
msgid "Edit bulk: plenary session"
msgstr "Massenbuchung: Plenarsitzung bearbeiten"

msgid "Edited attendences"
msgstr "Anwesenheiten bearbeitet"
Expand All @@ -924,8 +930,8 @@ msgid "Delete Attendencess"
msgstr "Anwesenheiten löschen"

#, python-format
msgid "Edit ${type}"
msgstr "${type} bearbeiten"
msgid "Edit bulk: ${type}"
msgstr "Massenbuchung: ${type} bearbeiten"

#, python-format
msgid "Cannot edit attendance - abschluss already set for: ${names}"
Expand All @@ -950,6 +956,9 @@ msgstr ""
msgid "Your changes were saved"
msgstr "Änderungen gespeichert"

msgid "Cannot delete individual bulk attendance."
msgstr "Einzelne Massenbuchung kann nicht gelöscht werden."

msgid "Cannot delete attendance in closed settlement run."
msgstr ""
"Anwesenheit in geschlossenem Abrechnungslauf kann nicht gelöscht werden."
Expand Down Expand Up @@ -1096,12 +1105,33 @@ msgstr ""
"Geschlossener Abrechnungslauf kann nicht gelöscht werden. Bitte zuerst "
"öffnen."

#~ msgid "Edit plenary session"
#~ msgstr "Plenarsitzung bearbeiten"

#, python-format
#~ msgid "Edit ${type}"
#~ msgstr "${type} bearbeiten"

#~ msgid "User Account Sync"
#~ msgstr "Benutzerkonto-Synchronisation"

#~ msgid "Synced"
#~ msgstr "Synchronisiert"

#~ msgid "Skipped"
#~ msgstr "Übersprungen"

#~ msgid "Created"
#~ msgstr "Erstellt"

#~ msgid "New accounts"
#~ msgstr "Neue Konten"

#~ msgid "No president or vice president found"
#~ msgstr "Kein Präsident oder Vizepräsident gefunden"

#~ msgid "President: ${name} (CHF ${amount})"
#~ msgstr "Präsident/in: ${name} (CHF ${amount})"

#~ msgid "Vice president: ${name} (CHF ${amount})"
#~ msgstr "Vizepräsident/in: ${name} (CHF ${amount})"

Expand Down
12 changes: 9 additions & 3 deletions src/onegov/pas/templates/attendences.pt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<table tal:condition="python:len(attendences) > 0" class="attendences hover">
<thead>
<tr>
<th i18n:translate>Date</th>
<th i18n:translate>Session date</th>
<th i18n:translate>Duration</th>
<th i18n:translate>Parliamentarian</th>
<th i18n:translate>Type</th>
Expand All @@ -18,13 +18,19 @@
</thead>
<tbody>
<tr tal:repeat="attendence attendences">
<td>${layout.format_date(attendence.date, 'date')}</td>
<td>${layout.format_date(attendence.date, 'date')}
<tal:b tal:condition="attendence.modified"><br>
<small class="muted"><tal:b i18n:translate>edited</tal:b>: ${layout.format_date(attendence.modified, 'date')}</small>
</tal:b>
</td>
<td>${(attendence.duration/60)} h</td>
<td>${attendence.parliamentarian.title}</td>
<td>${attendence.type_label}
<tal:block tal:condition="attendence.commission"><br>
<i>${attendence.commission.title}</i></tal:block></td>
<td><a href="${request.link(attendence)}"><i class="fa fa-edit"></i></a></td>
<td tal:condition="not:attendence.bulk_edit_id"><a href="${request.link(attendence)}"><i class="fa fa-edit"></i></a></td>
<td tal:condition="attendence.bulk_edit_id"
tal:define="bulk_name python:'edit-plenary-bulk-attendences' if attendence.type == 'plenary' else 'edit-commission-bulk-attendences'"><a href="${request.link(attendence, bulk_name)}"><i class="fa fa-edit"></i></a></td>
</tr>
</tbody>
</table>
Expand Down
61 changes: 26 additions & 35 deletions src/onegov/pas/views/attendence.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
from __future__ import annotations

from itertools import groupby
from operator import attrgetter
from collections import defaultdict
import uuid

from more_itertools import flatten

from onegov.core.elements import BackLink, Confirm, Intercooler, Link
from onegov.core.security import Private
from onegov.pas import _
Expand Down Expand Up @@ -50,41 +47,23 @@ def view_attendences(

layout = AttendenceCollectionLayout(self, request)

# Apply role-based filtering, then re-sort for bulk edit grouping
filtered_attendences = self.view_for_parliamentarian(request)
bulk_edit_attendences = sorted(
filtered_attendences,
key=lambda x: (str(x.bulk_edit_id) if x.bulk_edit_id else '',
x.created or x.modified),
reverse=True
)
attendences = self.query_for_current_user(request)

bulk_edit_groups = [
sorted(group, key=attrgetter('created', 'modified'), reverse=True)
for bulk_edit_id, group in groupby(
bulk_edit_attendences, key=attrgetter('bulk_edit_id'))
]
groups: dict[str, list[Attendence]] = defaultdict(list)
for a in attendences:
if a.bulk_edit_id:
groups[str(a.bulk_edit_id)].append(a)

non_null_groups = [g for g in bulk_edit_groups if getattr(
g[0], 'bulk_edit_id', None) is not None]
null_groups = [g for g in bulk_edit_groups if getattr(
g[0], 'bulk_edit_id', None) is None]

non_null_groups.sort(
key=lambda group: max( # type: ignore
(attendence.modified or attendence.created
for attendence in group),
default=None
),
reverse=True
bulk_edit_groups = sorted(
groups.values(),
key=lambda g: max(a.modified or a.created for a in g),
reverse=True,
)

bulk_edit_groups = non_null_groups + null_groups

return {
'add_link': request.link(self, name='new'),
'layout': layout,
'attendences': list(flatten(bulk_edit_groups)),
'attendences': attendences,
'title': layout.title,
'bulk_edit_groups': bulk_edit_groups
}
Expand Down Expand Up @@ -290,7 +269,7 @@ def edit_plenary_bulk_attendence(
request.alert(error)
return {
'layout': AttendenceCollectionLayout(self, request),
'title': _('Edit plenary session'),
'title': _('Edit bulk: plenary session'),
'form': form,
'form_width': 'large'
}
Expand Down Expand Up @@ -366,7 +345,7 @@ def edit_plenary_bulk_attendence(

return {
'layout': layout,
'title': _('Edit plenary session'),
'title': _('Edit bulk: plenary session'),
'form': form,
'form_width': 'large'
}
Expand All @@ -387,7 +366,7 @@ def edit_commission_bulk_attendence(
request.include('custom')

type_label = request.translate(self.type_label)
title = _('Edit ${type}', mapping={'type': type_label})
title = _('Edit bulk: ${type}', mapping={'type': type_label})

if form.submitted(request):
if form.date.data:
Expand Down Expand Up @@ -616,6 +595,14 @@ def edit_attendence(
form: AttendenceForm
) -> RenderData | Response:

if self.bulk_edit_id:
name = (
'edit-plenary-bulk-attendences'
if self.type == 'plenary'
else 'edit-commission-bulk-attendences'
)
return request.redirect(request.link(self, name))

if form.submitted(request):
if form.date.data:
if error := validate_attendance_date(
Expand Down Expand Up @@ -682,6 +669,10 @@ def delete_attendence(

request.assert_valid_csrf_token()

if self.bulk_edit_id:
request.alert(_('Cannot delete individual bulk attendance.'))
return

# Check if attendance is in a closed settlement run
settlement_run = request.session.query(SettlementRun).filter(
SettlementRun.start <= self.date,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ body {
font-size: 7pt;
text-decoration: underline;
margin-left: 0;
margin-bottom: 0;
margin-top: -0.3cm;
margin-bottom: 0.1cm;
}

.address {
margin-left: 0;
margin-top: -0.3cm;
margin-bottom: 0.5cm;
font-size: 8pt;
line-height: 1.4;
line-height: 1.2;
}

.date {
Expand Down
Loading