Passed
Push — main ( f182d8...d3b156 )
by Jochen
04:16
created

update_area()   A

Complexity

Conditions 2

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 5.0053

Importance

Changes 0
Metric Value
cc 2
eloc 17
nop 6
dl 0
loc 23
ccs 1
cts 11
cp 0.0909
crap 5.0053
rs 9.55
c 0
b 0
f 0
1
"""
2
byceps.services.seating.seating_area_service
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2014-2023 Jochen Kupperschmidt
6
:License: Revised BSD (see `LICENSE` file for details)
7
"""
8
9 1
from typing import Optional
10
11 1
from sqlalchemy import delete, select
12
13 1
from ...database import db
14 1
from ...typing import PartyID
15
16 1
from ..ticketing.dbmodels.ticket import DbTicket
17
18 1
from .dbmodels.area import DbSeatingArea
19 1
from .dbmodels.seat import DbSeat
20 1
from .models import SeatingArea, SeatingAreaID, SeatUtilization
21
22
23 1
def create_area(
24
    party_id: PartyID,
25
    slug: str,
26
    title: str,
27
    *,
28
    image_filename: Optional[str] = None,
29
    image_width: Optional[int] = None,
30
    image_height: Optional[int] = None,
31
) -> SeatingArea:
32
    """Create an area."""
33 1
    db_area = DbSeatingArea(
34
        party_id,
35
        slug,
36
        title,
37
        image_filename=image_filename,
38
        image_width=image_width,
39
        image_height=image_height,
40
    )
41
42 1
    db.session.add(db_area)
43 1
    db.session.commit()
44
45 1
    return _db_entity_to_area(db_area)
46
47
48 1
def update_area(
49
    area_id: SeatingAreaID,
50
    slug: str,
51
    title: str,
52
    image_filename: Optional[str],
53
    image_width: Optional[int],
54
    image_height: Optional[int],
55
) -> SeatingArea:
56
    """Update an area."""
57
    db_area = _find_db_area(area_id)
58
59
    if db_area is None:
60
        raise ValueError(f'Unknown seating area ID "{area_id}"')
61
62
    db_area.slug = slug
63
    db_area.title = title
64
    db_area.image_filename = image_filename
65
    db_area.image_width = image_width
66
    db_area.image_height = image_height
67
68
    db.session.commit()
69
70
    return _db_entity_to_area(db_area)
71
72
73 1
def delete_area(area_id: SeatingAreaID) -> None:
74
    """Delete an area."""
75 1
    db.session.execute(delete(DbSeatingArea).filter_by(id=area_id))
76 1
    db.session.commit()
77
78
79 1
def count_areas_for_party(party_id: PartyID) -> int:
80
    """Return the number of seating areas for that party."""
81 1
    return db.session.scalar(
82
        select(db.func.count(DbSeatingArea.id)).filter_by(party_id=party_id)
83
    )
84
85
86 1
def find_area(area_id: SeatingAreaID) -> Optional[SeatingArea]:
87
    """Return the area, or `None` if not found."""
88
    db_area = _find_db_area(area_id)
89
90
    if db_area is None:
91
        return None
92
93
    return _db_entity_to_area(db_area)
94
95
96 1
def _find_db_area(area_id: SeatingAreaID) -> Optional[DbSeatingArea]:
97
    return db.session.get(DbSeatingArea, area_id)
98
99
100 1
def find_area_for_party_by_slug(
101
    party_id: PartyID, slug: str
102
) -> Optional[SeatingArea]:
103
    """Return the area for that party with that slug, or `None` if not found."""
104
    db_area = db.session.scalars(
105
        select(DbSeatingArea).filter_by(party_id=party_id).filter_by(slug=slug)
106
    ).first()
107
108
    if db_area is None:
109
        return None
110
111
    return _db_entity_to_area(db_area)
112
113
114 1
def get_areas_with_seat_utilization(
115
    party_id: PartyID,
116
) -> list[tuple[SeatingArea, SeatUtilization]]:
117
    """Return all areas and their seat utilization for that party."""
118 1
    area = db.aliased(DbSeatingArea)
119
120 1
    subquery_occupied_seat_count = (
121
        select(db.func.count(DbTicket.id))
122
        .filter(DbTicket.revoked == False)  # noqa: E712
123
        .filter(DbTicket.occupied_seat_id.is_not(None))
124
        .join(DbSeat)
125
        .filter(DbSeat.area_id == area.id)
126
        .scalar_subquery()
127
    )
128
129 1
    subquery_total_seat_count = (
130
        select(db.func.count(DbSeat.id))
131
        .filter_by(area_id=area.id)
132
        .scalar_subquery()
133
    )
134
135 1
    rows = db.session.execute(
136
        select(
137
            area,
138
            subquery_occupied_seat_count,
139
            subquery_total_seat_count,
140
        )
141
        .filter(area.party_id == party_id)
142
        .group_by(area.id)
143
        .order_by(area.title)
144
    ).all()
145
146 1
    return [
147
        (
148
            _db_entity_to_area(db_area),
149
            SeatUtilization(
150
                occupied=occupied_seat_count, total=total_seat_count
151
            ),
152
        )
153
        for db_area, occupied_seat_count, total_seat_count in rows
154
    ]
155
156
157 1
def _db_entity_to_area(db_area: DbSeatingArea) -> SeatingArea:
158 1
    return SeatingArea(
159
        id=db_area.id,
160
        party_id=db_area.party_id,
161
        slug=db_area.slug,
162
        title=db_area.title,
163
        image_filename=db_area.image_filename,
164
        image_width=db_area.image_width,
165
        image_height=db_area.image_height,
166
    )
167