Skip to content

Commit 908e6ad

Browse files
committed
fix(Officers): merged the public and private models
1 parent 1d7a4c9 commit 908e6ad

4 files changed

Lines changed: 39 additions & 72 deletions

File tree

src/officers/crud.py

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@
1010
from auth.tables import SiteUserDB
1111
from data import semesters
1212
from officers.constants import OfficerPosition, OfficerPositionEnum
13-
from officers.models import OfficerCreate, OfficerPrivate, OfficerPublic
13+
from officers.models import Officer, OfficerCreate
1414
from officers.tables import OfficerInfoDB, OfficerTermDB
1515

1616
# NOTE: this module should not do any data validation; that should be done in the urls.py or higher layer
1717

1818

19-
async def current_officers(
20-
db_session: database.DBSession, include_private: bool = False
21-
) -> list[OfficerPrivate] | list[OfficerPublic]:
19+
async def current_officers(db_session: database.DBSession, include_private: bool = False) -> list[Officer]:
2220
"""
2321
Get info about officers that are active. Go through all active & complete officer terms.
2422
"""
@@ -35,7 +33,7 @@ async def current_officers(
3533
if include_private:
3634
for term, officer in result:
3735
officer_list.append(
38-
OfficerPrivate(
36+
Officer(
3937
legal_name=officer.legal_name,
4038
is_active=True,
4139
position=term.position,
@@ -55,17 +53,7 @@ async def current_officers(
5553
)
5654
else:
5755
for term, officer in result:
58-
officer_list.append(
59-
OfficerPublic(
60-
legal_name=officer.legal_name,
61-
is_active=True,
62-
position=term.position,
63-
start_date=term.start_date,
64-
end_date=term.end_date,
65-
biography=term.biography,
66-
csss_email=OfficerPosition.to_email(term.position),
67-
)
68-
)
56+
officer_list.append(Officer.public_fields(term, officer))
6957

7058
return officer_list
7159

@@ -96,7 +84,7 @@ async def get_current_terms_by_position(
9684

9785
async def get_all_officers(
9886
db_session: AsyncSession, include_future_terms: bool, include_private: bool
99-
) -> list[OfficerPrivate] | list[OfficerPublic]:
87+
) -> list[Officer]:
10088
"""
10189
This could be a lot of data, so be careful
10290
"""
@@ -114,7 +102,7 @@ async def get_all_officers(
114102
if include_private:
115103
for term, officer in result:
116104
officer_list.append(
117-
OfficerPrivate(
105+
Officer(
118106
legal_name=officer.legal_name,
119107
is_active=utils.is_active_term(term),
120108
position=term.position,
@@ -134,17 +122,7 @@ async def get_all_officers(
134122
)
135123
else:
136124
for term, officer in result:
137-
officer_list.append(
138-
OfficerPublic(
139-
legal_name=officer.legal_name,
140-
is_active=utils.is_active_term(term),
141-
position=term.position,
142-
start_date=term.start_date,
143-
end_date=term.end_date,
144-
biography=term.biography,
145-
csss_email=OfficerPosition.to_email(term.position),
146-
)
147-
)
125+
officer_list.append(Officer.public_fields(term, officer))
148126

149127
return officer_list
150128

src/officers/models.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from datetime import date
2+
from typing import Self
23

34
from pydantic import BaseModel, ConfigDict, Field
45

56
from constants import COMPUTING_ID_LEN
6-
from officers.constants import OFFICER_LEGAL_NAME_MAX, OfficerPositionEnum
7+
from officers.constants import OFFICER_LEGAL_NAME_MAX, OfficerPosition, OfficerPositionEnum
8+
from officers.tables import OfficerInfoDB, OfficerTermDB
79

810
OFFICER_PRIVATE_INFO = {
911
"discord_id",
@@ -83,23 +85,26 @@ class OfficerBase(BaseModel):
8385
csss_email: str | None = None
8486

8587

86-
class OfficerPublic(OfficerBase):
87-
"""
88-
Response when fetching public officer data
89-
"""
88+
class Officer(OfficerBase):
89+
@classmethod
90+
def public_fields(cls, term: OfficerTermDB, info: OfficerInfoDB) -> Self:
91+
return cls(
92+
legal_name=info.legal_name,
93+
is_active=True,
94+
position=term.position,
95+
start_date=term.start_date,
96+
end_date=term.end_date,
97+
biography=term.biography,
98+
csss_email=OfficerPosition.to_email(term.position),
99+
)
90100

91101
is_active: bool
92102

93-
94-
class OfficerPrivate(OfficerPublic):
95-
"""
96-
Response when fetching private officer data
97-
"""
98-
103+
# Private Info
99104
discord_id: str | None = None
100105
discord_name: str | None = None
101106
discord_nickname: str | None = None
102-
computing_id: str
107+
computing_id: str | None = None
103108
phone_number: str | None = None
104109
github_username: str | None = None
105110
google_drive_email: str | None = None

src/officers/tables.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
)
2323
from database import Base
2424
from officers.constants import OFFICER_LEGAL_NAME_MAX, OFFICER_POSITION_MAX, OfficerPositionEnum
25-
from officers.models import OfficerInfo, OfficerTerm, OfficerTermUpdate, OfficerUpdate
2625

2726

2827
# A row represents an assignment of a person to a position.
@@ -54,19 +53,6 @@ class OfficerTermDB(Base):
5453

5554
__table_args__ = (UniqueConstraint("computing_id", "position", "start_date"),) # This needs a comma to work
5655

57-
def serializable_dict(self) -> dict:
58-
return OfficerTerm.model_validate(self).model_dump(mode="json")
59-
60-
def update_from_params(self, params: OfficerTermUpdate, admin_update: bool = True):
61-
if admin_update:
62-
update_data = params.model_dump(exclude_unset=True)
63-
else:
64-
update_data = params.model_dump(
65-
exclude_unset=True, exclude={"position", "start_date", "end_date", "photo_url"}
66-
)
67-
for k, v in update_data.items():
68-
setattr(self, k, v)
69-
7056
def is_filled_in(self):
7157
return (
7258
# photo & end_date don't have to be uploaded for the term to be "filled"
@@ -131,9 +117,6 @@ class OfficerInfoDB(Base):
131117
# TODO (#22): add support for giving executives bitwarden access automagically
132118
# has_signed_into_bitwarden: Mapped[str] = mapped_column(Boolean)
133119

134-
def serializable_dict(self) -> dict[str, Any]:
135-
return OfficerInfo.model_validate(self).model_dump(mode="json")
136-
137120
def update_from_params(self, params: OfficerUpdate | OfficerUpdate):
138121
update_data = params.model_dump(exclude_unset=True)
139122
for k, v in update_data.items():

src/officers/urls.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
import officers.crud
77
from dependencies import LoggedInUser, SessionUser, perm_admin
88
from officers.models import (
9+
Officer,
910
OfficerCreate,
1011
OfficerInfo,
11-
OfficerPrivate,
12-
OfficerPublic,
1312
OfficerTerm,
1413
OfficerTermUpdate,
1514
OfficerUpdate,
@@ -54,7 +53,7 @@ async def _has_officer_private_info_access(
5453
@router.get(
5554
"/current",
5655
description="Get information about the current officers. With no authorization, only get basic info.",
57-
response_model=list[OfficerPrivate] | list[OfficerPublic],
56+
response_model=list[Officer],
5857
operation_id="get_current_officers",
5958
)
6059
async def current_officers(
@@ -65,15 +64,13 @@ async def current_officers(
6564

6665
curr_officers = await officers.crud.current_officers(db_session, has_private_access)
6766

68-
res = [o.model_dump(mode="json") for o in curr_officers]
69-
70-
return JSONResponse(res)
67+
return JSONResponse([o.model_dump(mode="json", exclude_unset=True) for o in curr_officers])
7168

7269

7370
@router.get(
7471
"/all",
7572
description="Information for all execs from all exec terms",
76-
response_model=list[OfficerPrivate] | list[OfficerPublic],
73+
response_model=list[Officer],
7774
responses={403: {"description": "not authorized", "model": DetailModel}},
7875
operation_id="get_all_officers",
7976
)
@@ -90,7 +87,7 @@ async def all_officers(
9087

9188
all_officers = await officers.crud.get_all_officers(db_session, include_future_terms, has_private_access)
9289

93-
return JSONResponse([officer_data.model_dump(mode="json") for officer_data in all_officers])
90+
return JSONResponse([officer_data.model_dump(mode="json", exclude_unset=True) for officer_data in all_officers])
9491

9592

9693
@router.get(
@@ -114,7 +111,9 @@ async def get_officer_terms(
114111

115112
# all term info is public, so anyone can get any of it
116113
officer_terms = await officers.crud.get_officer_terms(db_session, computing_id, include_future_terms)
117-
return JSONResponse([OfficerTerm.model_validate(term).model_dump(mode="json") for term in officer_terms])
114+
return JSONResponse(
115+
[OfficerTerm.model_validate(term).model_dump(mode="json", exclude_unset=True) for term in officer_terms]
116+
)
118117

119118

120119
@router.get(
@@ -133,7 +132,7 @@ async def get_officer_info(
133132
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="not authorized")
134133

135134
officer_info = await officers.crud.get_officer_info_or_raise(db_session, computing_id)
136-
return JSONResponse(officer_info.serializable_dict())
135+
return JSONResponse(OfficerInfo.model_validate(officer_info).model_dump(mode="json", exclude_unset=True))
137136

138137

139138
@router.post(
@@ -155,7 +154,7 @@ async def create_officer_term(
155154
officer_list: list[OfficerCreate],
156155
):
157156
new_terms = await officers.crud.create_multiple_officers(db_session, officer_list)
158-
content = [term.serializable_dict() for term in new_terms]
157+
content = [OfficerTerm.model_validate(term).model_dump(mode="json", exclude_unset=True) for term in new_terms]
159158

160159
await db_session.commit()
161160
return JSONResponse(content)
@@ -184,15 +183,17 @@ async def update_officer_info(
184183
await verify_update(user_id, db_session, computing_id)
185184

186185
old_officer_info = await officers.crud.get_officer_info_or_raise(db_session, computing_id)
187-
old_officer_info.update_from_params(officer_info_upload)
186+
update_data = officer_info_upload.model_dump(exclude_unset=True)
187+
for k, v in update_data.items():
188+
setattr(old_officer_info, k, v)
188189
await officers.crud.update_officer_info(db_session, old_officer_info)
189190

190191
# TODO (#27): log all important changes just to a .log file & persist them for a few years
191192

192193
await db_session.commit()
193194

194195
updated_officer_info = await officers.crud.get_new_officer_info_or_raise(db_session, computing_id)
195-
return JSONResponse(updated_officer_info.serializable_dict())
196+
return JSONResponse(OfficerInfo.model_validate(updated_officer_info).model_dump(mode="json", exclude_unset=True))
196197

197198

198199
@router.patch(
@@ -229,7 +230,7 @@ async def update_officer_term(db_session: database.DBSession, term_id: int, body
229230
await db_session.commit()
230231
await db_session.refresh(old_officer_term)
231232

232-
return JSONResponse(OfficerTerm.model_validate(old_officer_term).model_dump(mode="json"))
233+
return JSONResponse(OfficerTerm.model_validate(old_officer_term).model_dump(mode="json", exclude_unset=True))
233234

234235

235236
@router.delete(

0 commit comments

Comments
 (0)