byceps.blueprints.site.seating.views.index()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 3.1852

Importance

Changes 0
Metric Value
cc 2
eloc 8
nop 0
dl 0
loc 12
ccs 2
cts 6
cp 0.3333
crap 3.1852
rs 10
c 0
b 0
f 0
1
"""
2
byceps.blueprints.site.seating.views
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2006-2021 Jochen Kupperschmidt
6
:License: Revised BSD (see `LICENSE` file for details)
7
"""
8
9 1
from flask import abort, g, request
10 1
from flask_babel import gettext
11
12 1
from ....services.party import service as party_service
13 1
from ....services.seating import area_service as seating_area_service
14 1
from ....services.seating.dbmodels.seat import Seat
15 1
from ....services.seating import seat_service
16 1
from ....services.seating.transfer.models import SeatID
17 1
from ....services.ticketing.dbmodels.ticket import Ticket as DbTicket
18 1
from ....services.ticketing import (
19
    exceptions as ticket_exceptions,
20
    ticket_seat_management_service,
21
    ticket_service,
22
)
23 1
from ....services.ticketing.transfer.models import TicketID
24 1
from ....util.authorization import has_current_user_permission
25 1
from ....util.framework.blueprint import create_blueprint
26 1
from ....util.framework.flash import flash_error, flash_success
27 1
from ....util.framework.templating import templated
28 1
from ....util.views import login_required, redirect_to, respond_no_content
29
30 1
from . import service
31
32
33 1
blueprint = create_blueprint('seating', __name__)
34
35
36 1
@blueprint.get('/')
37 1
@templated
38
def index():
39
    """List areas."""
40
    if g.party_id is None:
41
        # No party is configured for the current site.
42
        abort(404)
43
44
    areas = seating_area_service.get_areas_for_party(g.party_id)
45
46
    return {
47
        'areas': areas,
48
    }
49
50
51 1
@blueprint.get('/areas/<slug>')
52 1
@templated
53
def view_area(slug):
54
    """View area."""
55
    if g.party_id is None:
56
        # No party is configured for the current site.
57
        abort(404)
58
59
    area = seating_area_service.find_area_for_party_by_slug(g.party_id, slug)
60
    if area is None:
61
        abort(404)
62
63
    seat_management_enabled = _is_seat_management_enabled()
64
65
    seats_with_tickets = seat_service.get_seats_with_tickets_for_area(area.id)
66
67
    users_by_id = service.get_users(seats_with_tickets, [])
68
69
    seats_and_tickets = service.get_seats_and_tickets(
70
        seats_with_tickets, users_by_id
71
    )
72
73
    return {
74
        'area': area,
75
        'seat_management_enabled': seat_management_enabled,
76
        'seats_and_tickets': seats_and_tickets,
77
        'manage_mode': False,
78
    }
79
80
81 1
@blueprint.get('/areas/<slug>/manage_seats')
82 1
@login_required
83 1
@templated('site/seating/view_area')
84
def manage_seats_in_area(slug):
85
    """Manage seats for assigned tickets in area."""
86
    if not _is_seat_management_enabled():
87
        flash_error(
88
            gettext('Seat reservations cannot be changed at this time.')
89
        )
90
        return redirect_to('.view_area', slug=slug)
91
92
    area = seating_area_service.find_area_for_party_by_slug(g.party_id, slug)
93
    if area is None:
94
        abort(404)
95
96
    seat_management_enabled = _is_seat_management_enabled()
97
98
    seat_manager_id = None
99
    selected_ticket_id = None
100
    selected_ticket = None
101
102
    if _is_current_user_seating_admin():
103
        selected_ticket = _get_selected_ticket()
104
        if selected_ticket is not None:
105
            seat_manager_id = selected_ticket.get_seat_manager().id
106
            selected_ticket_id = selected_ticket.id
107
        elif seat_management_enabled:
108
            seat_manager_id = g.user.id
109
110
    elif seat_management_enabled:
111
        seat_manager_id = g.user.id
112
113
    seats_with_tickets = seat_service.get_seats_with_tickets_for_area(area.id)
114
115
    if seat_manager_id is not None:
116
        tickets = ticket_service.find_tickets_for_seat_manager(
117
            seat_manager_id, g.party_id
118
        )
119
    else:
120
        tickets = []
121
122
    users_by_id = service.get_users(seats_with_tickets, tickets)
123
124
    seats_and_tickets = service.get_seats_and_tickets(
125
        seats_with_tickets, users_by_id
126
    )
127
128
    if seat_management_enabled:
129
        managed_tickets = list(
130
            service.get_managed_tickets(tickets, users_by_id)
131
        )
132
    else:
133
        managed_tickets = []
134
135
    return {
136
        'area': area,
137
        'seats_and_tickets': seats_and_tickets,
138
        'manage_mode': True,
139
        'seat_management_enabled': seat_management_enabled,
140
        'managed_tickets': managed_tickets,
141
        'selected_ticket_id': selected_ticket_id,
142
    }
143
144
145 1
def _get_selected_ticket():
146
    selected_ticket = None
147
148
    selected_ticket_id_arg = request.args.get('ticket_id')
149
    if selected_ticket_id_arg:
150
        selected_ticket = ticket_service.find_ticket(selected_ticket_id_arg)
151
        if selected_ticket is None:
152
            flash_error(
153
                gettext(
154
                    'Ticket ID "%(selected_ticket_id_arg)s" not found.',
155
                    selected_ticket_id_arg=selected_ticket_id_arg,
156
                )
157
            )
158
159
    if (selected_ticket is not None) and selected_ticket.revoked:
160
        flash_error(
161
            gettext(
162
                'Ticket "%(selected_ticket_code)s" is revoked.',
163
                selected_ticket_code=selected_ticket.code,
164
            )
165
        )
166
        selected_ticket = None
167
168
    return selected_ticket
169
170
171 1
@blueprint.post('/ticket/<uuid:ticket_id>/seat/<uuid:seat_id>')
172 1
@login_required
173 1
@respond_no_content
174
def occupy_seat(ticket_id, seat_id):
175
    """Use ticket to occupy seat."""
176
    if not _is_seat_management_enabled():
177
        flash_error(
178
            gettext('Seat reservations cannot be changed at this time.')
179
        )
180
        return
181
182
    ticket = _get_ticket_or_404(ticket_id)
183
184
    manager = g.user
185
186
    if (
187
        not ticket.is_seat_managed_by(manager.id)
188
        and not _is_current_user_seating_admin()
189
    ):
190
        flash_error(
191
            gettext(
192
                'You are not authorized to manage the seat for ticket %(ticket_code)s.',
193
                ticket_code=ticket.code,
194
            )
195
        )
196
        return
197
198
    seat = _get_seat_or_404(seat_id)
199
200
    if ticket_service.find_ticket_occupying_seat(seat.id) is not None:
201
        flash_error(
202
            gettext(
203
                '%(seat_label)s is already occupied.', seat_label=seat.label
204
            )
205
        )
206
        return
207
208
    try:
209
        ticket_seat_management_service.occupy_seat(
210
            ticket.id, seat.id, manager.id
211
        )
212
    except ticket_exceptions.SeatChangeDeniedForBundledTicket:
213
        flash_error(
214
            gettext(
215
                'Ticket %(ticket_code)s belongs to a bundle and cannot be managed separately.',
216
                ticket_code=ticket.code,
217
            )
218
        )
219
        return
220
    except ticket_exceptions.TicketCategoryMismatch:
221
        flash_error(
222
            gettext(
223
                'Ticket %(ticket_code)s and seat "%(seat_label)s" belong to different categories.',
224
                ticket_code=ticket.code,
225
                seat_label=seat.label,
226
            )
227
        )
228
        return
229
    except ValueError:
230
        abort(404)
231
232
    flash_success(
233
        gettext(
234
            '%(seat_label)s has been occupied with ticket %(ticket_code)s.',
235
            seat_label=seat.label,
236
            ticket_code=ticket.code,
237
        )
238
    )
239
240
241 1
@blueprint.delete('/ticket/<uuid:ticket_id>/seat')
242 1
@login_required
243 1
@respond_no_content
244
def release_seat(ticket_id):
245
    """Release the seat."""
246
    if not _is_seat_management_enabled():
247
        flash_error(
248
            gettext('Seat reservations cannot be changed at this time.')
249
        )
250
        return
251
252
    ticket = _get_ticket_or_404(ticket_id)
253
254
    if not ticket.occupied_seat:
255
        flash_error(
256
            gettext(
257
                'Ticket %(ticket_code)s occupies no seat.',
258
                ticket_code=ticket.code,
259
            )
260
        )
261
        return
262
263
    manager = g.user
264
265
    if (
266
        not ticket.is_seat_managed_by(manager.id)
267
        and not _is_current_user_seating_admin()
268
    ):
269
        flash_error(
270
            gettext(
271
                'You are not authorized to manage the seat for ticket %(ticket_code)s.',
272
                ticket_code=ticket.code,
273
            )
274
        )
275
        return
276
277
    seat = ticket.occupied_seat
278
279
    try:
280
        ticket_seat_management_service.release_seat(ticket.id, manager.id)
281
    except ticket_exceptions.SeatChangeDeniedForBundledTicket:
282
        flash_error(
283
            gettext(
284
                'Ticket %(ticket_code)s belongs to a bundle and cannot be managed separately.',
285
                ticket_code=ticket.code,
286
            )
287
        )
288
        return
289
290
    flash_success(
291
        gettext('%(seat_label)s has been released.', seat_label=seat.label)
292
    )
293
294
295 1
def _is_seat_management_enabled():
296
    if not g.user.authenticated:
297
        return False
298
299
    if g.party_id is None:
300
        return False
301
302
    if _is_current_user_seating_admin():
303
        return True
304
305
    party = party_service.get_party(g.party_id)
306
    return party.seat_management_enabled
307
308
309 1
def _is_current_user_seating_admin() -> bool:
310
    return has_current_user_permission('seating.administrate')
311
312
313 1
def _get_ticket_or_404(ticket_id: TicketID) -> DbTicket:
314
    ticket = ticket_service.find_ticket(ticket_id)
315
316
    if (ticket is None) or ticket.revoked:
317
        abort(404)
318
319
    return ticket
320
321
322 1
def _get_seat_or_404(seat_id: SeatID) -> Seat:
323
    seat = seat_service.find_seat(seat_id)
324
325
    if seat is None:
326
        abort(404)
327
328
    return seat
329