byceps.blueprints.site.seating.views   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 325
Duplicated Lines 0 %

Test Coverage

Coverage 23.97%

Importance

Changes 0
Metric Value
eloc 208
dl 0
loc 325
ccs 35
cts 146
cp 0.2397
rs 8.96
c 0
b 0
f 0
wmc 43

10 Functions

Rating   Name   Duplication   Size   Complexity  
A index() 0 12 2
C manage_seats_in_area() 0 59 9
B release_seat() 0 51 6
C occupy_seat() 0 66 8
A view_area() 0 25 3
A _is_current_user_seating_admin() 0 2 1
A _get_selected_ticket() 0 24 5
A _get_seat_or_404() 0 7 2
A _is_seat_management_enabled() 0 12 4
A _get_ticket_or_404() 0 7 3

How to fix   Complexity   

Complexity

Complex classes like byceps.blueprints.site.seating.views often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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 = seat_service.get_seats_with_tickets_for_area(area.id)
66
67
    users_by_id = service.get_users(seats, [])
68
69
    seats_and_tickets = service.get_seats_and_tickets(seats, users_by_id)
70
71
    return {
72
        'area': area,
73
        'seat_management_enabled': seat_management_enabled,
74
        'seats_and_tickets': seats_and_tickets,
75
        'manage_mode': False,
76
    }
77
78
79 1
@blueprint.get('/areas/<slug>/manage_seats')
80 1
@login_required
81 1
@templated('site/seating/view_area')
82
def manage_seats_in_area(slug):
83
    """Manage seats for assigned tickets in area."""
84
    if not _is_seat_management_enabled():
85
        flash_error(
86
            gettext('Seat reservations cannot be changed at this time.')
87
        )
88
        return redirect_to('.view_area', slug=slug)
89
90
    area = seating_area_service.find_area_for_party_by_slug(g.party_id, slug)
91
    if area is None:
92
        abort(404)
93
94
    seat_management_enabled = _is_seat_management_enabled()
95
96
    seat_manager_id = None
97
    selected_ticket_id = None
98
    selected_ticket = None
99
100
    if _is_current_user_seating_admin():
101
        selected_ticket = _get_selected_ticket()
102
        if selected_ticket is not None:
103
            seat_manager_id = selected_ticket.get_seat_manager().id
104
            selected_ticket_id = selected_ticket.id
105
        elif seat_management_enabled:
106
            seat_manager_id = g.user.id
107
108
    elif seat_management_enabled:
109
        seat_manager_id = g.user.id
110
111
    seats = seat_service.get_seats_with_tickets_for_area(area.id)
112
113
    if seat_manager_id is not None:
114
        tickets = ticket_service.find_tickets_for_seat_manager(
115
            seat_manager_id, g.party_id
116
        )
117
    else:
118
        tickets = []
119
120
    users_by_id = service.get_users(seats, tickets)
121
122
    seats_and_tickets = service.get_seats_and_tickets(seats, users_by_id)
123
124
    if seat_management_enabled:
125
        managed_tickets = list(
126
            service.get_managed_tickets(tickets, users_by_id)
127
        )
128
    else:
129
        managed_tickets = []
130
131
    return {
132
        'area': area,
133
        'seats_and_tickets': seats_and_tickets,
134
        'manage_mode': True,
135
        'seat_management_enabled': seat_management_enabled,
136
        'managed_tickets': managed_tickets,
137
        'selected_ticket_id': selected_ticket_id,
138
    }
139
140
141 1
def _get_selected_ticket():
142
    selected_ticket = None
143
144
    selected_ticket_id_arg = request.args.get('ticket_id')
145
    if selected_ticket_id_arg:
146
        selected_ticket = ticket_service.find_ticket(selected_ticket_id_arg)
147
        if selected_ticket is None:
148
            flash_error(
149
                gettext(
150
                    'Ticket ID "%(selected_ticket_id_arg)s" not found.',
151
                    selected_ticket_id_arg=selected_ticket_id_arg,
152
                )
153
            )
154
155
    if (selected_ticket is not None) and selected_ticket.revoked:
156
        flash_error(
157
            gettext(
158
                'Ticket "%(selected_ticket_code)s" is revoked.',
159
                selected_ticket_code=selected_ticket.code,
160
            )
161
        )
162
        selected_ticket = None
163
164
    return selected_ticket
165
166
167 1
@blueprint.post('/ticket/<uuid:ticket_id>/seat/<uuid:seat_id>')
168 1
@login_required
169 1
@respond_no_content
170
def occupy_seat(ticket_id, seat_id):
171
    """Use ticket to occupy seat."""
172
    if not _is_seat_management_enabled():
173
        flash_error(
174
            gettext('Seat reservations cannot be changed at this time.')
175
        )
176
        return
177
178
    ticket = _get_ticket_or_404(ticket_id)
179
180
    manager = g.user
181
182
    if (
183
        not ticket.is_seat_managed_by(manager.id)
184
        and not _is_current_user_seating_admin()
185
    ):
186
        flash_error(
187
            gettext(
188
                'You are not authorized to manage the seat for ticket %(ticket_code)s.',
189
                ticket_code=ticket.code,
190
            )
191
        )
192
        return
193
194
    seat = _get_seat_or_404(seat_id)
195
196
    if ticket_service.find_ticket_occupying_seat(seat.id) is not None:
197
        flash_error(
198
            gettext(
199
                '%(seat_label)s is already occupied.', seat_label=seat.label
200
            )
201
        )
202
        return
203
204
    try:
205
        ticket_seat_management_service.occupy_seat(
206
            ticket.id, seat.id, manager.id
207
        )
208
    except ticket_exceptions.SeatChangeDeniedForBundledTicket:
209
        flash_error(
210
            gettext(
211
                'Ticket %(ticket_code)s belongs to a bundle and cannot be managed separately.',
212
                ticket_code=ticket.code,
213
            )
214
        )
215
        return
216
    except ticket_exceptions.TicketCategoryMismatch:
217
        flash_error(
218
            gettext(
219
                'Ticket %(ticket_code)s and seat "%(seat_label)s" belong to different categories.',
220
                ticket_code=ticket.code,
221
                seat_label=seat.label,
222
            )
223
        )
224
        return
225
    except ValueError:
226
        abort(404)
227
228
    flash_success(
229
        gettext(
230
            '%(seat_label)s has been occupied with ticket %(ticket_code)s.',
231
            seat_label=seat.label,
232
            ticket_code=ticket.code,
233
        )
234
    )
235
236
237 1
@blueprint.delete('/ticket/<uuid:ticket_id>/seat')
238 1
@login_required
239 1
@respond_no_content
240
def release_seat(ticket_id):
241
    """Release the seat."""
242
    if not _is_seat_management_enabled():
243
        flash_error(
244
            gettext('Seat reservations cannot be changed at this time.')
245
        )
246
        return
247
248
    ticket = _get_ticket_or_404(ticket_id)
249
250
    if not ticket.occupied_seat:
251
        flash_error(
252
            gettext(
253
                'Ticket %(ticket_code)s occupies no seat.',
254
                ticket_code=ticket.code,
255
            )
256
        )
257
        return
258
259
    manager = g.user
260
261
    if (
262
        not ticket.is_seat_managed_by(manager.id)
263
        and not _is_current_user_seating_admin()
264
    ):
265
        flash_error(
266
            gettext(
267
                'You are not authorized to manage the seat for ticket %(ticket_code)s.',
268
                ticket_code=ticket.code,
269
            )
270
        )
271
        return
272
273
    seat = ticket.occupied_seat
274
275
    try:
276
        ticket_seat_management_service.release_seat(ticket.id, manager.id)
277
    except ticket_exceptions.SeatChangeDeniedForBundledTicket:
278
        flash_error(
279
            gettext(
280
                'Ticket %(ticket_code)s belongs to a bundle and cannot be managed separately.',
281
                ticket_code=ticket.code,
282
            )
283
        )
284
        return
285
286
    flash_success(
287
        gettext('%(seat_label)s has been released.', seat_label=seat.label)
288
    )
289
290
291 1
def _is_seat_management_enabled():
292
    if not g.user.authenticated:
293
        return False
294
295
    if g.party_id is None:
296
        return False
297
298
    if _is_current_user_seating_admin():
299
        return True
300
301
    party = party_service.get_party(g.party_id)
302
    return party.seat_management_enabled
303
304
305 1
def _is_current_user_seating_admin() -> bool:
306
    return has_current_user_permission('seating.administrate')
307
308
309 1
def _get_ticket_or_404(ticket_id: TicketID) -> DbTicket:
310
    ticket = ticket_service.find_ticket(ticket_id)
311
312
    if (ticket is None) or ticket.revoked:
313
        abort(404)
314
315
    return ticket
316
317
318 1
def _get_seat_or_404(seat_id: SeatID) -> Seat:
319
    seat = seat_service.find_seat(seat_id)
320
321
    if seat is None:
322
        abort(404)
323
324
    return seat
325