Completed
Push — main ( 765c3c...3fba7c )
by Jochen
05:30
created

byceps.blueprints.seating.views._is_seating_admin()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1.125

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 1
dl 0
loc 2
ccs 1
cts 2
cp 0.5
crap 1.125
rs 10
c 0
b 0
f 0
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 typing import Dict, Iterator, Sequence
10
11 1
from flask import abort, g
12
13 1
from ...services.party import service as party_service
14 1
from ...services.seating import area_service as seating_area_service
15 1
from ...services.seating.models.seat import Seat
16 1
from ...services.seating import seat_service
17 1
from ...services.seating.transfer.models import SeatID
18 1
from ...services.ticketing.models.ticket import Ticket as DbTicket
19 1
from ...services.ticketing import (
20
    exceptions as ticket_exceptions,
21
    ticket_seat_management_service,
22
    ticket_service,
23
)
24 1
from ...services.ticketing.transfer.models import TicketID
25 1
from ...services.user import service as user_service
26 1
from ...services.user.transfer.models import User
27 1
from ...typing import UserID
28 1
from ...util.framework.blueprint import create_blueprint
29 1
from ...util.framework.flash import flash_error, flash_success
30 1
from ...util.framework.templating import templated
31 1
from ...util.views import respond_no_content
32
33 1
from ..admin.seating.authorization import SeatingPermission
34 1
from ..authentication.decorators import login_required
35 1
from ..authorization.registry import permission_registry
36
37
38 1
blueprint = create_blueprint('seating', __name__)
39
40
41 1
permission_registry.register_enum(SeatingPermission)
42
43
44 1
@blueprint.route('/')
45 1
@templated
46
def index():
47
    """List areas."""
48
    if g.party_id is None:
49
        # No party is configured for the current site.
50
        abort(404)
51
52
    areas = seating_area_service.get_areas_for_party(g.party_id)
53
54
    return {
55
        'areas': areas,
56
    }
57
58
59 1
@blueprint.route('/areas/<slug>')
60 1
@templated
61
def view_area(slug):
62
    """View area."""
63
    if g.party_id is None:
64
        # No party is configured for the current site.
65
        abort(404)
66
67
    area = seating_area_service.find_area_for_party_by_slug(g.party_id, slug)
68
    if area is None:
69
        abort(404)
70
71
    seat_management_enabled = _is_seat_management_enabled()
72
73
    seats = seat_service.get_seats_with_tickets_for_area(area.id)
74
75
    if seat_management_enabled:
76
        managed_tickets = ticket_service.find_tickets_for_seat_manager(
77
            g.current_user.id, g.party_id
78
        )
79
    else:
80
        managed_tickets = []
81
82
    users_by_id = _get_users(seats, managed_tickets)
83
84
    return {
85
        'area': area,
86
        'seat_management_enabled': seat_management_enabled,
87
        'seats': seats,
88
        'managed_tickets': managed_tickets,
89
        'users_by_id': users_by_id,
90
    }
91
92
93 1
def _get_users(seats, managed_tickets):
94
    return {}  # Not implemented.
95
96
97 1
@blueprint.route(
98
    '/ticket/<uuid:ticket_id>/seat/<uuid:seat_id>', methods=['POST']
99
)
100 1
@login_required
101 1
@respond_no_content
102
def occupy_seat(ticket_id, seat_id):
103
    """Use ticket to occupy seat."""
104
    _abort_if_seat_management_disabled()
105
106
    ticket = _get_ticket_or_404(ticket_id)
107
108
    manager = g.current_user
109
110
    if not ticket.is_seat_managed_by(manager.id) and not _is_seating_admin(
111
        manager
112
    ):
113
        flash_error(
114
            'Du bist nicht berechtigt, den Sitzplatz '
115
            f'für Ticket {ticket.code} zu verwalten.'
116
        )
117
        return
118
119
    seat = _get_seat_or_404(seat_id)
120
121
    if seat.is_occupied:
122
        flash_error(f'{seat.label} ist bereits belegt.')
123
        return
124
125
    try:
126
        ticket_seat_management_service.occupy_seat(
127
            ticket.id, seat.id, manager.id
128
        )
129
    except ticket_exceptions.SeatChangeDeniedForBundledTicket:
130
        flash_error(
131
            f'Ticket {ticket.code} gehört zu einem Paket '
132
            'und kann nicht einzeln verwaltet werden.'
133
        )
134
        return
135
    except ticket_exceptions.TicketCategoryMismatch:
136
        flash_error(
137
            f'Ticket {ticket.code} und {seat.label} haben '
138
            'unterschiedliche Kategorien.'
139
        )
140
        return
141
    except ValueError:
142
        abort(404)
143
144
    flash_success(f'{seat.label} wurde mit Ticket {ticket.code} reserviert.')
145
146
147 1
@blueprint.route('/ticket/<uuid:ticket_id>/seat', methods=['DELETE'])
148 1
@login_required
149 1
@respond_no_content
150
def release_seat(ticket_id):
151
    """Release the seat."""
152
    _abort_if_seat_management_disabled()
153
154
    ticket = _get_ticket_or_404(ticket_id)
155
156
    if not ticket.occupied_seat:
157
        flash_error(f'Ticket {ticket.code} belegt keinen Sitzplatz.')
158
        return
159
160
    manager = g.current_user
161
162
    if not ticket.is_seat_managed_by(manager.id) and not _is_seating_admin(
163
        manager
164
    ):
165
        flash_error(
166
            'Du bist nicht berechtigt, den Sitzplatz '
167
            f'für Ticket {ticket.code} zu verwalten.'
168
        )
169
        return
170
171
    seat = ticket.occupied_seat
172
173
    try:
174
        ticket_seat_management_service.release_seat(ticket.id, manager.id)
175
    except ticket_exceptions.SeatChangeDeniedForBundledTicket:
176
        flash_error(
177
            f'Ticket {ticket.code} gehört zu einem Paket '
178
            'und kann nicht einzeln verwaltet werden.'
179
        )
180
        return
181
182
    flash_success(f'{seat.label} wurde freigegeben.')
183
184
185 1
def _abort_if_seat_management_disabled() -> None:
186
    if not _is_seat_management_enabled():
187
        flash_error('Sitzplätze können derzeit nicht verändert werden.')
188
        return
189
190
191 1
def _is_seat_management_enabled():
192
    if g.current_user.is_anonymous:
193
        return False
194
195
    if g.party_id is None:
196
        return False
197
198
    if _is_seating_admin(g.current_user):
199
        return True
200
201
    party = party_service.get_party(g.party_id)
202
    return party.seat_management_enabled
203
204
205 1
def _is_seating_admin(user) -> bool:
206
    return user.has_permission(SeatingPermission.administrate)
207
208
209 1
def _get_ticket_or_404(ticket_id: TicketID) -> DbTicket:
210
    ticket = ticket_service.find_ticket(ticket_id)
211
212
    if (ticket is None) or ticket.revoked:
213
        abort(404)
214
215
    return ticket
216
217
218 1
def _get_seat_or_404(seat_id: SeatID) -> Seat:
219
    seat = seat_service.find_seat(seat_id)
220
221
    if seat is None:
222
        abort(404)
223
224
    return seat
225