~homeworkprod/byceps

ref: 342e5296ae417372e544ce931b442e98301c17a1 byceps/byceps/blueprints/admin/orga_team/views.py -rw-r--r-- 11.4 KiB
342e5296 — Jochen Kupperschmidt Require body blocks in base templates 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
"""
byceps.blueprints.admin.orga_team.views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:Copyright: 2006-2021 Jochen Kupperschmidt
:License: Revised BSD (see `LICENSE` file for details)
"""

from flask import abort, request
from flask_babel import gettext

from ....services.orga_team import service as orga_team_service
from ....services.party import service as party_service
from ....services.user import service as user_service
from ....util.authorization import register_permission_enum
from ....util.framework.blueprint import create_blueprint
from ....util.framework.flash import flash_error, flash_success
from ....util.framework.templating import templated
from ....util.views import permission_required, redirect_to, respond_no_content

from .authorization import OrgaTeamPermission
from .forms import (
    MembershipCreateForm,
    MembershipUpdateForm,
    OrgaTeamCreateForm,
    OrgaTeamsCopyForm,
)


blueprint = create_blueprint('orga_team_admin', __name__)


register_permission_enum(OrgaTeamPermission)


# -------------------------------------------------------------------- #
# teams


@blueprint.route('/teams/<party_id>')
@permission_required(OrgaTeamPermission.view)
@templated
def teams_for_party(party_id):
    """List organizer teams for that party."""
    party = _get_party_or_404(party_id)

    teams_and_members = orga_team_service.get_teams_and_members_for_party(
        party.id
    )

    def sort_members(members):
        return sorted(
            members,
            key=lambda m: user_service.get_sort_key_for_screen_name(m.user),
        )

    teams_and_members = sorted(teams_and_members, key=lambda tam: tam[0].title)
    teams_and_members = [
        (teams, sort_members(members)) for teams, members in teams_and_members
    ]

    return {
        'party': party,
        'teams_and_members': teams_and_members,
    }


@blueprint.route('/teams/<party_id>/create')
@permission_required(OrgaTeamPermission.create)
@templated
def team_create_form(party_id, erroneous_form=None):
    """Show form to create an organizer team for a party."""
    party = _get_party_or_404(party_id)

    form = erroneous_form if erroneous_form else OrgaTeamCreateForm()

    return {
        'party': party,
        'form': form,
    }


@blueprint.route('/teams/<party_id>', methods=['POST'])
@permission_required(OrgaTeamPermission.create)
def team_create(party_id):
    """Create an organizer team for a party."""
    party = _get_party_or_404(party_id)

    form = OrgaTeamCreateForm(request.form)
    if not form.validate():
        return team_create_form(party.id, form)

    title = form.title.data.strip()

    team = orga_team_service.create_team(party.id, title)

    flash_success(
        gettext(
            'Team "%(team_title)s" for party "%(party_title)s" has been created.',
            team_title=team.title,
            party_title=party.title,
        )
    )
    return redirect_to('.teams_for_party', party_id=party.id)


@blueprint.route('/teams/<uuid:team_id>', methods=['DELETE'])
@permission_required(OrgaTeamPermission.delete)
@respond_no_content
def team_delete(team_id):
    """Delete the team."""
    team = _get_team_or_404(team_id)

    if orga_team_service.has_team_memberships(team.id):
        flash_error(
            gettext(
                'Team "%(team_title)s" cannot be deleted because it has members.',
                team_title=team.title,
            )
        )
        return

    title = team.title

    orga_team_service.delete_team(team.id)

    flash_success(gettext('Team "%(title)s" has been deleted.', title=title))


@blueprint.route('/teams/<target_party_id>/copy')
@permission_required(OrgaTeamPermission.create)
@templated
def teams_copy_form(target_party_id, erroneous_form=None):
    """Show form to copy all organizer teams from another party."""
    target_party = _get_party_or_404(target_party_id)

    team_count = orga_team_service.count_teams_for_party(target_party.id)
    if team_count:
        flash_error(
            gettext(
                'This party already has teams. No additional teams can be copied to it.'
            )
        )
        return redirect_to('.teams_for_party', party_id=target_party.id)

    parties = party_service.get_parties_for_brand(target_party.brand_id)

    # Do not offer to copy teams from target party.
    parties = [p for p in parties if p.id != target_party.id]

    party_ids = {party.id for party in parties}
    team_count_per_party = orga_team_service.count_teams_for_parties(party_ids)

    # Exclude parties without orga teams.
    parties = [p for p in parties if team_count_per_party.get(p.id, 0)]

    if not parties:
        flash_error(
            gettext('No other parties exist from which teams could be copied.')
        )
        return redirect_to('.teams_for_party', party_id=target_party.id)

    parties.sort(key=lambda p: p.starts_at, reverse=True)

    form = erroneous_form if erroneous_form else OrgaTeamsCopyForm()
    form.set_party_choices(parties, team_count_per_party)

    return {
        'party': target_party,
        'form': form,
    }


@blueprint.route('/teams/<target_party_id>/copy', methods=['POST'])
@permission_required(OrgaTeamPermission.create)
def teams_copy(target_party_id):
    """Copy all organizer teams from another party."""
    target_party = _get_party_or_404(target_party_id)

    target_team_count = orga_team_service.count_teams_for_party(target_party.id)
    if target_team_count:
        flash_error(
            gettext(
                'This party already has teams. No additional teams can be copied to it.'
            )
        )
        return redirect_to('.teams_for_party', party_id=target_party.id)

    parties = party_service.get_parties_for_brand(target_party.brand_id)

    form = OrgaTeamsCopyForm(request.form)
    form.set_party_choices(parties)
    if not form.validate():
        return teams_copy_form(target_party.id, form)

    source_party = party_service.get_party(form.party_id.data)

    copied_teams_count = orga_team_service.copy_teams_and_memberships(
        source_party.id, target_party.id
    )

    flash_success(
        gettext(
            '%(copied_teams_count)s team(s) has/have been copied from party '
            '"%(source_party_title)s" to party "%(target_party_title)s".',
            copied_teams_count=copied_teams_count,
            source_party_title=source_party.title,
            target_party_title=target_party.title,
        )
    )

    return redirect_to('.teams_for_party', party_id=target_party.id)


# -------------------------------------------------------------------- #
# memberships


@blueprint.route('/teams/<uuid:team_id>/memberships/create')
@permission_required(OrgaTeamPermission.administrate_memberships)
@templated
def membership_create_form(team_id, erroneous_form=None):
    """Show form to assign an organizer to that team."""
    team = _get_team_or_404(team_id)

    party = party_service.get_party(team.party_id)

    unassigned_orgas = orga_team_service.get_unassigned_orgas_for_party(
        team.party_id
    )

    if not unassigned_orgas:
        return {
            'team': team,
            'party': party,
            'unassigned_orgas_available': False,
        }

    unassigned_orgas = sorted(
        unassigned_orgas, key=user_service.get_sort_key_for_screen_name
    )

    form = erroneous_form if erroneous_form else MembershipCreateForm()
    form.set_user_choices(unassigned_orgas)

    return {
        'form': form,
        'team': team,
        'party': party,
        'unassigned_orgas_available': True,
    }


@blueprint.route('/teams/<uuid:team_id>/memberships', methods=['POST'])
@permission_required(OrgaTeamPermission.administrate_memberships)
def membership_create(team_id):
    """Assign an organizer to that team."""
    team = _get_team_or_404(team_id)

    unassigned_orgas = orga_team_service.get_unassigned_orgas_for_party(
        team.party_id
    )

    form = MembershipCreateForm(request.form)
    form.set_user_choices(unassigned_orgas)

    if not form.validate():
        return membership_create_form(team.id, form)

    user = user_service.get_user(form.user_id.data)
    duties = form.duties.data.strip()

    membership = orga_team_service.create_membership(team.id, user.id, duties)

    flash_success(
        gettext(
            '%(screen_name)s has been added to team "%(team_title)s".',
            screen_name=user.screen_name,
            team_title=team.title,
        )
    )
    return redirect_to('.teams_for_party', party_id=team.party_id)


@blueprint.route('/memberships/<uuid:membership_id>/update')
@permission_required(OrgaTeamPermission.administrate_memberships)
@templated
def membership_update_form(membership_id, erroneous_form=None):
    """Show form to update a membership."""
    membership = _get_membership_or_404(membership_id)

    user = user_service.get_user(membership.user_id)
    team = orga_team_service.find_team(membership.orga_team_id)
    party = party_service.get_party(team.party_id)

    teams = orga_team_service.get_teams_for_party(team.party_id)

    form = (
        erroneous_form
        if erroneous_form
        else MembershipUpdateForm(obj=membership)
    )
    form.set_orga_team_choices(teams)

    return {
        'form': form,
        'membership': membership,
        'user': user,
        'team': team,
        'party': party,
    }


@blueprint.route('/memberships/<uuid:membership_id>', methods=['POST'])
@permission_required(OrgaTeamPermission.administrate_memberships)
def membership_update(membership_id):
    """Update a membership."""
    membership = _get_membership_or_404(membership_id)

    user = user_service.get_user(membership.user_id)
    team = orga_team_service.find_team(membership.orga_team_id)

    teams = orga_team_service.get_teams_for_party(team.party_id)

    form = MembershipUpdateForm(request.form)
    form.set_orga_team_choices(teams)

    if not form.validate():
        return membership_update_form(membership.id, form)

    team_id = form.orga_team_id.data
    team = orga_team_service.find_team(team_id)
    duties = form.duties.data.strip() or None

    orga_team_service.update_membership(membership.id, team.id, duties)

    flash_success(
        gettext(
            'Membership of %(screen_name)s has been updated.',
            screen_name=user.screen_name,
        )
    )
    return redirect_to('.teams_for_party', party_id=team.party_id)


@blueprint.route('/memberships/<uuid:membership_id>', methods=['DELETE'])
@permission_required(OrgaTeamPermission.administrate_memberships)
@respond_no_content
def membership_remove(membership_id):
    """Remove an organizer from a team."""
    membership = _get_membership_or_404(membership_id)

    user = user_service.get_user(membership.user_id)
    team = orga_team_service.find_team(membership.orga_team_id)

    orga_team_service.delete_membership(membership.id)

    flash_success(
        gettext(
            '%(screen_name)s has been removed from team "%(team_title)s".',
            screen_name=user.screen_name,
            team_title=team.title,
        )
    )


# -------------------------------------------------------------------- #
# helpers


def _get_party_or_404(party_id):
    party = party_service.find_party(party_id)

    if party is None:
        abort(404)

    return party


def _get_team_or_404(team_id):
    team = orga_team_service.find_team(team_id)

    if team is None:
        abort(404)

    return team


def _get_membership_or_404(membership_id):
    membership = orga_team_service.find_membership(membership_id)

    if membership is None:
        abort(404)

    return membership