1
|
|
|
""" |
2
|
|
|
byceps.blueprints.admin.orga_team.views |
3
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
4
|
|
|
|
5
|
|
|
:Copyright: 2006-2020 Jochen Kupperschmidt |
6
|
|
|
:License: Modified BSD, see LICENSE for details. |
7
|
|
|
""" |
8
|
|
|
|
9
|
1 |
|
from flask import abort, request |
10
|
|
|
|
11
|
1 |
|
from ....services.orga_team import service as orga_team_service |
12
|
1 |
|
from ....services.party import service as party_service |
13
|
1 |
|
from ....services.user import service as user_service |
14
|
1 |
|
from ....util.framework.blueprint import create_blueprint |
15
|
1 |
|
from ....util.framework.flash import flash_error, flash_success |
16
|
1 |
|
from ....util.framework.templating import templated |
17
|
1 |
|
from ....util.views import redirect_to, respond_no_content |
18
|
|
|
|
19
|
1 |
|
from ...common.authorization.decorators import permission_required |
20
|
1 |
|
from ...common.authorization.registry import permission_registry |
21
|
|
|
|
22
|
1 |
|
from .authorization import OrgaTeamPermission |
23
|
1 |
|
from .forms import ( |
24
|
|
|
MembershipCreateForm, |
25
|
|
|
MembershipUpdateForm, |
26
|
|
|
OrgaTeamCreateForm, |
27
|
|
|
OrgaTeamsCopyForm, |
28
|
|
|
) |
29
|
|
|
|
30
|
|
|
|
31
|
1 |
|
blueprint = create_blueprint('orga_team_admin', __name__) |
32
|
|
|
|
33
|
|
|
|
34
|
1 |
|
permission_registry.register_enum(OrgaTeamPermission) |
35
|
|
|
|
36
|
|
|
|
37
|
|
|
# -------------------------------------------------------------------- # |
38
|
|
|
# teams |
39
|
|
|
|
40
|
|
|
|
41
|
1 |
|
@blueprint.route('/teams/<party_id>') |
42
|
1 |
|
@permission_required(OrgaTeamPermission.view) |
43
|
1 |
|
@templated |
44
|
|
|
def teams_for_party(party_id): |
45
|
|
|
"""List organizer teams for that party.""" |
46
|
|
|
party = _get_party_or_404(party_id) |
47
|
|
|
|
48
|
|
|
teams_and_members = orga_team_service.get_teams_and_members_for_party( |
49
|
|
|
party.id |
50
|
|
|
) |
51
|
|
|
|
52
|
|
|
def sort_members(members): |
53
|
|
|
return sorted( |
54
|
|
|
members, |
55
|
|
|
key=lambda m: user_service.get_sort_key_for_screen_name(m.user), |
56
|
|
|
) |
57
|
|
|
|
58
|
|
|
teams_and_members = sorted(teams_and_members, key=lambda tam: tam[0].title) |
59
|
|
|
teams_and_members = [ |
60
|
|
|
(teams, sort_members(members)) for teams, members in teams_and_members |
61
|
|
|
] |
62
|
|
|
|
63
|
|
|
return { |
64
|
|
|
'party': party, |
65
|
|
|
'teams_and_members': teams_and_members, |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
|
69
|
1 |
|
@blueprint.route('/teams/<party_id>/create') |
70
|
1 |
|
@permission_required(OrgaTeamPermission.create) |
71
|
1 |
|
@templated |
72
|
1 |
|
def team_create_form(party_id, erroneous_form=None): |
73
|
|
|
"""Show form to create an organizer team for a party.""" |
74
|
|
|
party = _get_party_or_404(party_id) |
75
|
|
|
|
76
|
|
|
form = erroneous_form if erroneous_form else OrgaTeamCreateForm() |
77
|
|
|
|
78
|
|
|
return { |
79
|
|
|
'party': party, |
80
|
|
|
'form': form, |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
|
84
|
1 |
|
@blueprint.route('/teams/<party_id>', methods=['POST']) |
85
|
1 |
|
@permission_required(OrgaTeamPermission.create) |
86
|
|
|
def team_create(party_id): |
87
|
|
|
"""Create an organizer team for a party.""" |
88
|
|
|
party = _get_party_or_404(party_id) |
89
|
|
|
|
90
|
|
|
form = OrgaTeamCreateForm(request.form) |
91
|
|
|
if not form.validate(): |
92
|
|
|
return team_create_form(party.id, form) |
93
|
|
|
|
94
|
|
|
title = form.title.data.strip() |
95
|
|
|
|
96
|
|
|
team = orga_team_service.create_team(party.id, title) |
97
|
|
|
|
98
|
|
|
flash_success( |
99
|
|
|
f'Das Team "{team.title}" wurde ' |
100
|
|
|
f'für die Party "{party.title}" erstellt.' |
101
|
|
|
) |
102
|
|
|
return redirect_to('.teams_for_party', party_id=party.id) |
103
|
|
|
|
104
|
|
|
|
105
|
1 |
|
@blueprint.route('/teams/<uuid:team_id>', methods=['DELETE']) |
106
|
1 |
|
@permission_required(OrgaTeamPermission.delete) |
107
|
1 |
|
@respond_no_content |
108
|
|
|
def team_delete(team_id): |
109
|
|
|
"""Delete the team.""" |
110
|
|
|
team = _get_team_or_404(team_id) |
111
|
|
|
|
112
|
|
|
if orga_team_service.has_team_memberships(team.id): |
113
|
|
|
flash_error( |
114
|
|
|
f'Orga team "{team.title}" cannot be deleted because it has members.' |
115
|
|
|
) |
116
|
|
|
return |
117
|
|
|
|
118
|
|
|
title = team.title |
119
|
|
|
|
120
|
|
|
orga_team_service.delete_team(team.id) |
121
|
|
|
|
122
|
|
|
flash_success(f'Das Team "{title}" wurde gelöscht.') |
123
|
|
|
|
124
|
|
|
|
125
|
1 |
|
@blueprint.route('/teams/<target_party_id>/copy') |
126
|
1 |
|
@permission_required(OrgaTeamPermission.create) |
127
|
1 |
|
@templated |
128
|
1 |
|
def teams_copy_form(target_party_id, erroneous_form=None): |
129
|
|
|
"""Show form to copy all organizer teams from another party.""" |
130
|
|
|
target_party = _get_party_or_404(target_party_id) |
131
|
|
|
|
132
|
|
|
team_count = orga_team_service.count_teams_for_party(target_party.id) |
133
|
|
|
if team_count: |
134
|
|
|
flash_error( |
135
|
|
|
'Diese Party hat bereits Orga-Teams. ' |
136
|
|
|
'Es können keine weiteren hinzu kopiert werden.' |
137
|
|
|
) |
138
|
|
|
return redirect_to('.teams_for_party', party_id=target_party.id) |
139
|
|
|
|
140
|
|
|
parties = party_service.get_parties_for_brand(target_party.brand_id) |
141
|
|
|
|
142
|
|
|
# Do not offer to copy teams from target party. |
143
|
|
|
parties = [p for p in parties if p.id != target_party.id] |
144
|
|
|
|
145
|
|
|
party_ids = {party.id for party in parties} |
146
|
|
|
team_count_per_party = orga_team_service.count_teams_for_parties(party_ids) |
147
|
|
|
|
148
|
|
|
# Exclude parties without orga teams. |
149
|
|
|
parties = [p for p in parties if team_count_per_party.get(p.id, 0)] |
150
|
|
|
|
151
|
|
|
if not parties: |
152
|
|
|
flash_error( |
153
|
|
|
'Es sind keine anderen Partys vorhanden, von denen Orga-Teams ' |
154
|
|
|
'kopiert werden können.' |
155
|
|
|
) |
156
|
|
|
return redirect_to('.teams_for_party', party_id=target_party.id) |
157
|
|
|
|
158
|
|
|
parties.sort(key=lambda p: p.starts_at, reverse=True) |
159
|
|
|
|
160
|
|
|
form = erroneous_form if erroneous_form else OrgaTeamsCopyForm() |
161
|
|
|
form.set_party_choices(parties, team_count_per_party) |
162
|
|
|
|
163
|
|
|
return { |
164
|
|
|
'party': target_party, |
165
|
|
|
'form': form, |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
|
169
|
1 |
|
@blueprint.route('/teams/<target_party_id>/copy', methods=['POST']) |
170
|
1 |
|
@permission_required(OrgaTeamPermission.create) |
171
|
|
|
def teams_copy(target_party_id): |
172
|
|
|
"""Copy all organizer teams from another party.""" |
173
|
|
|
target_party = _get_party_or_404(target_party_id) |
174
|
|
|
|
175
|
|
|
target_team_count = orga_team_service.count_teams_for_party(target_party.id) |
176
|
|
|
if target_team_count: |
177
|
|
|
flash_error( |
178
|
|
|
'Diese Party hat bereits Orga-Teams. ' |
179
|
|
|
'Es können keine weiteren hinzu kopiert werden.' |
180
|
|
|
) |
181
|
|
|
return redirect_to('.teams_for_party', party_id=target_party.id) |
182
|
|
|
|
183
|
|
|
parties = party_service.get_parties_for_brand(target_party.brand_id) |
184
|
|
|
|
185
|
|
|
form = OrgaTeamsCopyForm(request.form) |
186
|
|
|
form.set_party_choices(parties) |
187
|
|
|
if not form.validate(): |
188
|
|
|
return teams_copy_form(target_party.id, form) |
189
|
|
|
|
190
|
|
|
source_party = party_service.get_party(form.party_id.data) |
191
|
|
|
|
192
|
|
|
copied_teams_count = orga_team_service.copy_teams_and_memberships( |
193
|
|
|
source_party.id, target_party.id |
194
|
|
|
) |
195
|
|
|
|
196
|
|
|
flash_success( |
197
|
|
|
f'{copied_teams_count:d} Team(s) wurde von Party ' |
198
|
|
|
f'"{source_party.title}" zu Party "{target_party.title}" kopiert.' |
199
|
|
|
) |
200
|
|
|
|
201
|
|
|
return redirect_to('.teams_for_party', party_id=target_party.id) |
202
|
|
|
|
203
|
|
|
|
204
|
|
|
# -------------------------------------------------------------------- # |
205
|
|
|
# memberships |
206
|
|
|
|
207
|
|
|
|
208
|
1 |
|
@blueprint.route('/teams/<uuid:team_id>/memberships/create') |
209
|
1 |
|
@permission_required(OrgaTeamPermission.administrate_memberships) |
210
|
1 |
|
@templated |
211
|
1 |
|
def membership_create_form(team_id, erroneous_form=None): |
212
|
|
|
"""Show form to assign an organizer to that team.""" |
213
|
|
|
team = _get_team_or_404(team_id) |
214
|
|
|
|
215
|
|
|
party = party_service.get_party(team.party_id) |
216
|
|
|
|
217
|
|
|
unassigned_orgas = orga_team_service.get_unassigned_orgas_for_party( |
218
|
|
|
team.party_id |
219
|
|
|
) |
220
|
|
|
|
221
|
|
|
if not unassigned_orgas: |
222
|
|
|
return { |
223
|
|
|
'team': team, |
224
|
|
|
'party': party, |
225
|
|
|
'unassigned_orgas_available': False, |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
unassigned_orgas = sorted( |
229
|
|
|
unassigned_orgas, key=user_service.get_sort_key_for_screen_name |
230
|
|
|
) |
231
|
|
|
|
232
|
|
|
form = erroneous_form if erroneous_form else MembershipCreateForm() |
233
|
|
|
form.set_user_choices(unassigned_orgas) |
234
|
|
|
|
235
|
|
|
return { |
236
|
|
|
'form': form, |
237
|
|
|
'team': team, |
238
|
|
|
'party': party, |
239
|
|
|
'unassigned_orgas_available': True, |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
|
243
|
1 |
|
@blueprint.route('/teams/<uuid:team_id>/memberships', methods=['POST']) |
244
|
1 |
|
@permission_required(OrgaTeamPermission.administrate_memberships) |
245
|
|
|
def membership_create(team_id): |
246
|
|
|
"""Assign an organizer to that team.""" |
247
|
|
|
team = _get_team_or_404(team_id) |
248
|
|
|
|
249
|
|
|
unassigned_orgas = orga_team_service.get_unassigned_orgas_for_party( |
250
|
|
|
team.party_id |
251
|
|
|
) |
252
|
|
|
|
253
|
|
|
form = MembershipCreateForm(request.form) |
254
|
|
|
form.set_user_choices(unassigned_orgas) |
255
|
|
|
|
256
|
|
|
if not form.validate(): |
257
|
|
|
return membership_create_form(team.id, form) |
258
|
|
|
|
259
|
|
|
user = user_service.get_user(form.user_id.data) |
260
|
|
|
duties = form.duties.data.strip() |
261
|
|
|
|
262
|
|
|
membership = orga_team_service.create_membership(team.id, user.id, duties) |
263
|
|
|
|
264
|
|
|
flash_success( |
265
|
|
|
f'{user.screen_name} wurde in das Team "{team.title}" aufgenommen.' |
266
|
|
|
) |
267
|
|
|
return redirect_to('.teams_for_party', party_id=team.party_id) |
268
|
|
|
|
269
|
|
|
|
270
|
1 |
|
@blueprint.route('/memberships/<uuid:membership_id>/update') |
271
|
1 |
|
@permission_required(OrgaTeamPermission.administrate_memberships) |
272
|
1 |
|
@templated |
273
|
1 |
|
def membership_update_form(membership_id, erroneous_form=None): |
274
|
|
|
"""Show form to update a membership.""" |
275
|
|
|
membership = _get_membership_or_404(membership_id) |
276
|
|
|
|
277
|
|
|
user = user_service.get_user(membership.user_id) |
278
|
|
|
team = orga_team_service.find_team(membership.orga_team_id) |
279
|
|
|
party = party_service.get_party(team.party_id) |
280
|
|
|
|
281
|
|
|
teams = orga_team_service.get_teams_for_party(team.party_id) |
282
|
|
|
|
283
|
|
|
form = ( |
284
|
|
|
erroneous_form |
285
|
|
|
if erroneous_form |
286
|
|
|
else MembershipUpdateForm(obj=membership) |
287
|
|
|
) |
288
|
|
|
form.set_orga_team_choices(teams) |
289
|
|
|
|
290
|
|
|
return { |
291
|
|
|
'form': form, |
292
|
|
|
'membership': membership, |
293
|
|
|
'user': user, |
294
|
|
|
'team': team, |
295
|
|
|
'party': party, |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
|
299
|
1 |
|
@blueprint.route('/memberships/<uuid:membership_id>', methods=['POST']) |
300
|
1 |
|
@permission_required(OrgaTeamPermission.administrate_memberships) |
301
|
|
|
def membership_update(membership_id): |
302
|
|
|
"""Update a membership.""" |
303
|
|
|
membership = _get_membership_or_404(membership_id) |
304
|
|
|
|
305
|
|
|
user = user_service.get_user(membership.user_id) |
306
|
|
|
team = orga_team_service.find_team(membership.orga_team_id) |
307
|
|
|
|
308
|
|
|
teams = orga_team_service.get_teams_for_party(team.party_id) |
309
|
|
|
|
310
|
|
|
form = MembershipUpdateForm(request.form) |
311
|
|
|
form.set_orga_team_choices(teams) |
312
|
|
|
|
313
|
|
|
if not form.validate(): |
314
|
|
|
return membership_update_form(membership.id, form) |
315
|
|
|
|
316
|
|
|
team_id = form.orga_team_id.data |
317
|
|
|
team = orga_team_service.find_team(team_id) |
318
|
|
|
duties = form.duties.data.strip() or None |
319
|
|
|
|
320
|
|
|
orga_team_service.update_membership(membership.id, team.id, duties) |
321
|
|
|
|
322
|
|
|
flash_success( |
323
|
|
|
f'Die Teammitgliedschaft von {user.screen_name} wurde aktualisiert.' |
324
|
|
|
) |
325
|
|
|
return redirect_to('.teams_for_party', party_id=team.party_id) |
326
|
|
|
|
327
|
|
|
|
328
|
1 |
|
@blueprint.route('/memberships/<uuid:membership_id>', methods=['DELETE']) |
329
|
1 |
|
@permission_required(OrgaTeamPermission.administrate_memberships) |
330
|
1 |
|
@respond_no_content |
331
|
|
|
def membership_remove(membership_id): |
332
|
|
|
"""Remove an organizer from a team.""" |
333
|
|
|
membership = _get_membership_or_404(membership_id) |
334
|
|
|
|
335
|
|
|
user = user_service.get_user(membership.user_id) |
336
|
|
|
team = orga_team_service.find_team(membership.orga_team_id) |
337
|
|
|
|
338
|
|
|
orga_team_service.delete_membership(membership.id) |
339
|
|
|
|
340
|
|
|
flash_success( |
341
|
|
|
f'{user.screen_name} wurde aus dem Team "{team.title}" entfernt.' |
342
|
|
|
) |
343
|
|
|
|
344
|
|
|
|
345
|
|
|
# -------------------------------------------------------------------- # |
346
|
|
|
# helpers |
347
|
|
|
|
348
|
|
|
|
349
|
1 |
|
def _get_party_or_404(party_id): |
350
|
|
|
party = party_service.find_party(party_id) |
351
|
|
|
|
352
|
|
|
if party is None: |
353
|
|
|
abort(404) |
354
|
|
|
|
355
|
|
|
return party |
356
|
|
|
|
357
|
|
|
|
358
|
1 |
|
def _get_team_or_404(team_id): |
359
|
|
|
team = orga_team_service.find_team(team_id) |
360
|
|
|
|
361
|
|
|
if team is None: |
362
|
|
|
abort(404) |
363
|
|
|
|
364
|
|
|
return team |
365
|
|
|
|
366
|
|
|
|
367
|
1 |
|
def _get_membership_or_404(membership_id): |
368
|
|
|
membership = orga_team_service.find_membership(membership_id) |
369
|
|
|
|
370
|
|
|
if membership is None: |
371
|
|
|
abort(404) |
372
|
|
|
|
373
|
|
|
return membership |
374
|
|
|
|