1
|
|
|
""" |
2
|
|
|
byceps.blueprints.admin.orga_presence.views |
3
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
4
|
|
|
|
5
|
|
|
:Copyright: 2006-2021 Jochen Kupperschmidt |
6
|
|
|
:License: Revised BSD (see `LICENSE` file for details) |
7
|
|
|
""" |
8
|
|
|
|
9
|
1 |
|
from __future__ import annotations |
10
|
1 |
|
from collections import defaultdict |
11
|
1 |
|
from datetime import datetime |
12
|
1 |
|
from typing import Iterable |
13
|
|
|
|
14
|
1 |
|
from flask import abort, g, request |
15
|
|
|
|
16
|
1 |
|
from ....services.orga_presence import service as orga_presence_service |
17
|
1 |
|
from ....services.orga_presence.transfer.models import ( |
18
|
|
|
PartyTimeSlot, |
19
|
|
|
PresenceTimeSlot, |
20
|
|
|
TimeSlot, |
21
|
|
|
) |
22
|
1 |
|
from ....services.party import service as party_service |
23
|
1 |
|
from ....services.user.transfer.models import User |
24
|
1 |
|
from ....util.datetime.timezone import local_tz_to_utc |
25
|
1 |
|
from ....util.framework.blueprint import create_blueprint |
26
|
1 |
|
from ....util.framework.templating import templated |
27
|
1 |
|
from ....util.views import permission_required, redirect_to, respond_no_content |
28
|
|
|
|
29
|
1 |
|
from .forms import build_presence_create_form |
30
|
|
|
|
31
|
|
|
|
32
|
1 |
|
blueprint = create_blueprint('orga_presence', __name__) |
33
|
|
|
|
34
|
|
|
|
35
|
1 |
|
@blueprint.get('/<party_id>') |
36
|
1 |
|
@permission_required('orga_presence.view') |
37
|
1 |
|
@templated |
38
|
|
|
def view(party_id): |
39
|
|
|
"""List orga presence and task time slots for that party.""" |
40
|
|
|
party = _get_party_or_404(party_id) |
41
|
|
|
|
42
|
|
|
party_time_slot = PartyTimeSlot.from_party(party) |
43
|
|
|
presences = orga_presence_service.get_presences(party.id) |
44
|
|
|
tasks = orga_presence_service.get_tasks(party.id) |
45
|
|
|
|
46
|
|
|
presences_by_orga = _group_presences_by_orga(presences) |
47
|
|
|
|
48
|
|
|
time_slots = [party_time_slot] + presences + tasks |
49
|
|
|
hour_ranges = list(orga_presence_service.get_hour_ranges(time_slots)) |
50
|
|
|
|
51
|
|
|
days_and_hour_totals = list( |
52
|
|
|
orga_presence_service.get_days_and_hour_totals(hour_ranges) |
53
|
|
|
) |
54
|
|
|
|
55
|
|
|
current_user_presences = [ |
56
|
|
|
presence for presence in presences if presence.orga.id == g.user.id |
57
|
|
|
] |
58
|
|
|
|
59
|
|
|
return { |
60
|
|
|
'party': party, |
61
|
|
|
'days_and_hour_totals': days_and_hour_totals, |
62
|
|
|
'hour_ranges': hour_ranges, |
63
|
|
|
'party_time_slot': party_time_slot, |
64
|
|
|
'presences_by_orga': presences_by_orga, |
65
|
|
|
'tasks': tasks, |
66
|
|
|
'is_instant_contained_in_time_slots': is_instant_contained_in_time_slots, |
67
|
|
|
'current_user_presences': current_user_presences, |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
|
71
|
1 |
|
def _group_presences_by_orga( |
72
|
|
|
presences: Iterable[PresenceTimeSlot], |
73
|
|
|
) -> dict[User, set[PresenceTimeSlot]]: |
74
|
|
|
d = defaultdict(set) |
75
|
|
|
|
76
|
|
|
for presence in presences: |
77
|
|
|
d[presence.orga].add(presence) |
78
|
|
|
|
79
|
|
|
return d |
80
|
|
|
|
81
|
|
|
|
82
|
1 |
|
def is_instant_contained_in_time_slots( |
83
|
|
|
instant: datetime, time_slots: Iterable[TimeSlot] |
84
|
|
|
) -> bool: |
85
|
|
|
return any(time_slot.range.contains(instant) for time_slot in time_slots) |
86
|
|
|
|
87
|
|
|
|
88
|
1 |
|
@blueprint.get('/<party_id>/presences/create') |
89
|
1 |
|
@permission_required('orga_presence.update') |
90
|
1 |
|
@templated |
91
|
1 |
|
def create_form(party_id, erroneous_form=None): |
92
|
|
|
"""Show form to create a presence for that party.""" |
93
|
|
|
party = _get_party_or_404(party_id) |
94
|
|
|
|
95
|
|
|
party_time_slot = PartyTimeSlot.from_party(party) |
96
|
|
|
valid_range = party_time_slot.range |
97
|
|
|
|
98
|
|
|
CreateForm = build_presence_create_form(valid_range) |
99
|
|
|
form = erroneous_form if erroneous_form else CreateForm() |
100
|
|
|
|
101
|
|
|
return { |
102
|
|
|
'party': party, |
103
|
|
|
'valid_range': valid_range, |
104
|
|
|
'form': form, |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
|
108
|
1 |
|
@blueprint.post('/<party_id>/presences') |
109
|
1 |
|
@permission_required('orga_presence.update') |
110
|
|
|
def create(party_id): |
111
|
|
|
"""Create a presence for that party.""" |
112
|
|
|
party = _get_party_or_404(party_id) |
113
|
|
|
|
114
|
|
|
party_time_slot = PartyTimeSlot.from_party(party) |
115
|
|
|
|
116
|
|
|
CreateForm = build_presence_create_form(party_time_slot.range) |
117
|
|
|
form = CreateForm(request.form) |
118
|
|
|
|
119
|
|
|
if not form.validate(): |
120
|
|
|
return create_form(party.id, form) |
121
|
|
|
|
122
|
|
|
starts_at = local_tz_to_utc( |
123
|
|
|
datetime.combine(form.starts_on.data, form.starts_at.data) |
124
|
|
|
) |
125
|
|
|
ends_at = local_tz_to_utc( |
126
|
|
|
datetime.combine(form.ends_on.data, form.ends_at.data) |
127
|
|
|
) |
128
|
|
|
|
129
|
|
|
orga_presence_service.create_presence( |
130
|
|
|
party.id, |
131
|
|
|
g.user.id, |
132
|
|
|
starts_at, |
133
|
|
|
ends_at, |
134
|
|
|
) |
135
|
|
|
|
136
|
|
|
return redirect_to('.view', party_id=party.id) |
137
|
|
|
|
138
|
|
|
|
139
|
1 |
|
@blueprint.delete('/time_slots/<time_slot_id>') |
140
|
1 |
|
@permission_required('orga_presence.update') |
141
|
1 |
|
@respond_no_content |
142
|
|
|
def time_slot_delete(time_slot_id): |
143
|
|
|
"""Delete the time slot.""" |
144
|
|
|
orga_presence_service.delete_time_slot(time_slot_id) |
145
|
|
|
|
146
|
|
|
|
147
|
1 |
|
def _get_party_or_404(party_id): |
148
|
|
|
party = party_service.find_party(party_id) |
149
|
|
|
|
150
|
|
|
if party is None: |
151
|
|
|
abort(404) |
152
|
|
|
|
153
|
|
|
return party |
154
|
|
|
|