Completed
Push — main ( c065c4...1fd2bb )
by Jochen
04:54
created

byceps.blueprints.seating.views.manage_seats_in_area()   C

Complexity

Conditions 10

Size

Total Lines 71
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 88.6586

Importance

Changes 0
Metric Value
cc 10
eloc 50
nop 1
dl 0
loc 71
ccs 3
cts 39
cp 0.0769
crap 88.6586
rs 5.8362
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like byceps.blueprints.seating.views.manage_seats_in_area() 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.seating.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, g, request
10
11 1
from ...services.party import service as party_service
12 1
from ...services.seating import area_service as seating_area_service
13 1
from ...services.seating.models.seat import Seat
14 1
from ...services.seating import seat_service
15 1
from ...services.seating.transfer.models import SeatID
16 1
from ...services.ticketing.models.ticket import Ticket as DbTicket
17 1
from ...services.ticketing import (
18
    exceptions as ticket_exceptions,
19
    ticket_seat_management_service,
20
    ticket_service,
21
)
22 1
from ...services.ticketing.transfer.models import TicketID
23 1
from ...util.framework.blueprint import create_blueprint
24 1
from ...util.framework.flash import flash_error, flash_success
25 1
from ...util.framework.templating import templated
26 1
from ...util.views import respond_no_content
27
28 1
from ..admin.seating.authorization import SeatingPermission
29 1
from ..authentication.decorators import login_required
30 1
from ..authorization.registry import permission_registry
31
32 1
from . import service
33
34
35 1
blueprint = create_blueprint('seating', __name__)
36
37
38 1
permission_registry.register_enum(SeatingPermission)
39
40
41 1
@blueprint.route('/')
42 1
@templated
43
def index():
44
    """List areas."""
45
    if g.party_id is None:
46
        # No party is configured for the current site.
47
        abort(404)
48
49
    areas = seating_area_service.get_areas_for_party(g.party_id)
50
51
    return {
52
        'areas': areas,
53
    }
54
55
56 1
@blueprint.route('/areas/<slug>')
57 1
@templated
58
def view_area(slug):
59
    """View area."""
60
    if g.party_id is None:
61
        # No party is configured for the current site.
62
        abort(404)
63
64
    area = seating_area_service.find_area_for_party_by_slug(g.party_id, slug)
65
    if area is None:
66
        abort(404)
67
68
    seat_management_enabled = _is_seat_management_enabled()
69
70
    seats = seat_service.get_seats_with_tickets_for_area(area.id)
71
72
    users_by_id = service.get_users(seats, [])
73
74
    seats = service.get_seats(seats, users_by_id)
75
76
    return {
77
        'area': area,
78
        'seat_management_enabled': seat_management_enabled,
79
        'seats': seats,
80
        'manage_mode': False,
81
    }
82
83
84 1
@blueprint.route('/areas/<slug>/manage_seats')
85 1
@login_required
86 1
@templated('seating/view_area')
87
def manage_seats_in_area(slug):
88
    """Manage seats for assigned tickets in area."""
89
    _abort_if_seat_management_disabled()
90
91
    area = seating_area_service.find_area_for_party_by_slug(g.party_id, slug)
92
    if area is None:
93
        abort(404)
94
95
    seat_management_enabled = _is_seat_management_enabled()
96
97
    seats = seat_service.get_seats_with_tickets_for_area(area.id)
98
99
    ticket_code = request.args.get('ticket_code')
100
    selected_ticket_id = request.args.get('ticket_id')
101
102
    if _is_seating_admin(g.current_user) and ticket_code:
103
        ticket_code = ticket_code.upper()
104
        ticket = ticket_service.find_ticket_by_code(ticket_code)
105
        if ticket:
106
            tickets = ticket_service.find_tickets_for_seat_manager(
107
                ticket.get_seat_manager().id, g.party_id
108
            )
109
            selected_ticket_id = ticket.id
110
        else:
111
            flash_error(f'Ticket code "{ticket_code}" not found.')
112
            tickets = []
113
            selected_ticket_id = None
114
115
    elif _is_seating_admin(g.current_user) and selected_ticket_id:
116
        ticket = ticket_service.find_ticket(selected_ticket_id)
117
        if ticket:
118
            tickets = ticket_service.find_tickets_for_seat_manager(
119
                ticket.get_seat_manager().id, g.party_id
120
            )
121
            selected_ticket_id = ticket.id
122
        else:
123
            flash_error(f'Ticket ID "{selected_ticket_id}" not found.')
124
            tickets = []
125
            selected_ticket_id = None
126
127
    elif seat_management_enabled:
128
        tickets = ticket_service.find_tickets_for_seat_manager(
129
            g.current_user.id, g.party_id
130
        )
131
        selected_ticket_id = None
132
133
    else:
134
        tickets = []
135
        selected_ticket_id = None
136
137
    users_by_id = service.get_users(seats, tickets)
138
139
    seats = service.get_seats(seats, users_by_id)
140
141
    if seat_management_enabled:
142
        managed_tickets = list(
143
            service.get_managed_tickets(tickets, users_by_id)
144
        )
145
    else:
146
        managed_tickets = []
147
148
    return {
149
        'area': area,
150
        'seat_management_enabled': seat_management_enabled,
151
        'seats': seats,
152
        'manage_mode': True,
153
        'managed_tickets': managed_tickets,
154
        'selected_ticket_id': selected_ticket_id,
155
    }
156
157
158 1
def _get_users(seats, managed_tickets):
159
    return {}  # Not implemented.
160
161
162 1
@blueprint.route(
163
    '/ticket/<uuid:ticket_id>/seat/<uuid:seat_id>', methods=['POST']
164
)
165 1
@login_required
166 1
@respond_no_content
167
def occupy_seat(ticket_id, seat_id):
168
    """Use ticket to occupy seat."""
169
    _abort_if_seat_management_disabled()
170
171
    ticket = _get_ticket_or_404(ticket_id)
172
173
    manager = g.current_user
174
175
    if not ticket.is_seat_managed_by(manager.id) and not _is_seating_admin(
176
        manager
177
    ):
178
        flash_error(
179
            'Du bist nicht berechtigt, den Sitzplatz '
180
            f'für Ticket {ticket.code} zu verwalten.'
181
        )
182
        return
183
184
    seat = _get_seat_or_404(seat_id)
185
186
    if seat.is_occupied:
187
        flash_error(f'{seat.label} ist bereits belegt.')
188
        return
189
190
    try:
191
        ticket_seat_management_service.occupy_seat(
192
            ticket.id, seat.id, manager.id
193
        )
194
    except ticket_exceptions.SeatChangeDeniedForBundledTicket:
195
        flash_error(
196
            f'Ticket {ticket.code} gehört zu einem Paket '
197
            'und kann nicht einzeln verwaltet werden.'
198
        )
199
        return
200
    except ticket_exceptions.TicketCategoryMismatch:
201
        flash_error(
202
            f'Ticket {ticket.code} und {seat.label} haben '
203
            'unterschiedliche Kategorien.'
204
        )
205
        return
206
    except ValueError:
207
        abort(404)
208
209
    flash_success(f'{seat.label} wurde mit Ticket {ticket.code} reserviert.')
210
211
212 1
@blueprint.route('/ticket/<uuid:ticket_id>/seat', methods=['DELETE'])
213 1
@login_required
214 1
@respond_no_content
215
def release_seat(ticket_id):
216
    """Release the seat."""
217
    _abort_if_seat_management_disabled()
218
219
    ticket = _get_ticket_or_404(ticket_id)
220
221
    if not ticket.occupied_seat:
222
        flash_error(f'Ticket {ticket.code} belegt keinen Sitzplatz.')
223
        return
224
225
    manager = g.current_user
226
227
    if not ticket.is_seat_managed_by(manager.id) and not _is_seating_admin(
228
        manager
229
    ):
230
        flash_error(
231
            'Du bist nicht berechtigt, den Sitzplatz '
232
            f'für Ticket {ticket.code} zu verwalten.'
233
        )
234
        return
235
236
    seat = ticket.occupied_seat
237
238
    try:
239
        ticket_seat_management_service.release_seat(ticket.id, manager.id)
240
    except ticket_exceptions.SeatChangeDeniedForBundledTicket:
241
        flash_error(
242
            f'Ticket {ticket.code} gehört zu einem Paket '
243
            'und kann nicht einzeln verwaltet werden.'
244
        )
245
        return
246
247
    flash_success(f'{seat.label} wurde freigegeben.')
248
249
250 1
def _abort_if_seat_management_disabled() -> None:
251
    if not _is_seat_management_enabled():
252
        flash_error('Sitzplätze können derzeit nicht verändert werden.')
253
        return
254
255
256 1
def _is_seat_management_enabled():
257
    if g.current_user.is_anonymous:
258
        return False
259
260
    if g.party_id is None:
261
        return False
262
263
    if _is_seating_admin(g.current_user):
264
        return True
265
266
    party = party_service.get_party(g.party_id)
267
    return party.seat_management_enabled
268
269
270 1
def _is_seating_admin(user) -> bool:
271
    return user.has_permission(SeatingPermission.administrate)
272
273
274 1
def _get_ticket_or_404(ticket_id: TicketID) -> DbTicket:
275
    ticket = ticket_service.find_ticket(ticket_id)
276
277
    if (ticket is None) or ticket.revoked:
278
        abort(404)
279
280
    return ticket
281
282
283 1
def _get_seat_or_404(seat_id: SeatID) -> Seat:
284
    seat = seat_service.find_seat(seat_id)
285
286
    if seat is None:
287
        abort(404)
288
289
    return seat
290