Passed
Push — master ( 3058b1...ede4b9 )
by Jochen
02:15
created

byceps/blueprints/seating/views.py (1 issue)

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