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
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 |