Skip to content

Commit bd96ceb

Browse files
committed
clean up get officer info and get officer terms
1 parent 1ecd0e1 commit bd96ceb

File tree

4 files changed

+104
-96
lines changed

4 files changed

+104
-96
lines changed

src/officers/crud.py

Lines changed: 45 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -42,49 +42,6 @@ async def current_officer_positions(db_session: database.DBSession, computing_id
4242
officer_term_list = (await db_session.scalars(query)).all()
4343
return [term.position for term in officer_term_list]
4444

45-
async def officer_info(db_session: database.DBSession, computing_id: str) -> OfficerInfo:
46-
officer_term = await db_session.scalar(
47-
sqlalchemy
48-
.select(OfficerInfo)
49-
.where(OfficerInfo.computing_id == computing_id)
50-
)
51-
if officer_term is None:
52-
raise HTTPException(status_code=400, detail=f"officer_info for computing_id={computing_id} does not exist yet")
53-
return officer_term
54-
55-
async def officer_term(db_session: database.DBSession, term_id: int) -> OfficerTerm:
56-
query = (
57-
sqlalchemy
58-
.select(OfficerTerm)
59-
.where(OfficerTerm.id == term_id)
60-
)
61-
officer_term = await db_session.scalar(query)
62-
if officer_term is None:
63-
raise HTTPException(status_code=400, detail=f"Could not find officer_term with id={term_id}")
64-
return officer_term
65-
66-
async def get_officer_terms(
67-
db_session: database.DBSession,
68-
computing_id: str,
69-
max_terms: None | int,
70-
# will include term info for officers that are not active
71-
# or have not yet been filled out
72-
include_inactive: bool,
73-
) -> list[OfficerTerm]:
74-
query = (
75-
sqlalchemy
76-
.select(OfficerTerm)
77-
.where(OfficerTerm.computing_id == computing_id)
78-
.order_by(OfficerTerm.start_date.desc())
79-
)
80-
if not include_inactive:
81-
query = utils.is_active_officer(query)
82-
83-
if max_terms is not None:
84-
query.limit(max_terms)
85-
86-
return (await db_session.scalars(query)).all()
87-
8845
async def current_officers(
8946
db_session: database.DBSession,
9047
include_private: bool
@@ -125,7 +82,7 @@ async def current_officers(
12582
async def all_officers(
12683
db_session: database.DBSession,
12784
include_private_data: bool,
128-
view_not_started_officer_terms: bool,
85+
include_future_terms: bool,
12986
) -> list[OfficerData]:
13087
"""
13188
This could be a lot of data, so be careful
@@ -137,8 +94,8 @@ async def all_officers(
13794
# Ordered recent first
13895
.order_by(OfficerTerm.start_date.desc())
13996
)
140-
if not view_not_started_officer_terms:
141-
query = utils.has_term_started(query)
97+
if not include_future_terms:
98+
query = utils.has_started_term(query)
14299

143100
officer_data_list = []
144101
officer_terms = (await db_session.scalars(query)).all()
@@ -157,6 +114,47 @@ async def all_officers(
157114

158115
return officer_data_list
159116

117+
async def get_officer_info(db_session: database.DBSession, computing_id: str) -> OfficerInfo:
118+
officer_term = await db_session.scalar(
119+
sqlalchemy
120+
.select(OfficerInfo)
121+
.where(OfficerInfo.computing_id == computing_id)
122+
)
123+
if officer_term is None:
124+
raise HTTPException(status_code=400, detail=f"officer_info for computing_id={computing_id} does not exist yet")
125+
return officer_term
126+
127+
async def get_officer_terms(
128+
db_session: database.DBSession,
129+
computing_id: str,
130+
max_terms: None | int,
131+
include_future_terms: bool,
132+
) -> list[OfficerTerm]:
133+
query = (
134+
sqlalchemy
135+
.select(OfficerTerm)
136+
.where(OfficerTerm.computing_id == computing_id)
137+
.order_by(OfficerTerm.start_date.desc())
138+
)
139+
if not include_future_terms:
140+
query = utils.has_started_term(query)
141+
142+
if max_terms is not None:
143+
query.limit(max_terms)
144+
145+
return (await db_session.scalars(query)).all()
146+
147+
async def get_officer_term_by_id(db_session: database.DBSession, term_id: int) -> OfficerTerm:
148+
query = (
149+
sqlalchemy
150+
.select(OfficerTerm)
151+
.where(OfficerTerm.id == term_id)
152+
)
153+
officer_term = await db_session.scalar(query)
154+
if officer_term is None:
155+
raise HTTPException(status_code=400, detail=f"Could not find officer_term with id={term_id}")
156+
return officer_term
157+
160158
async def create_new_officer_info(
161159
db_session: database.DBSession,
162160
new_officer_info: OfficerInfo
@@ -246,4 +244,5 @@ async def update_officer_term(
246244
return True
247245

248246
async def remove_officer_term():
247+
# TODO: implement this
249248
pass

src/officers/tables.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class OfficerTerm(Base):
4747
favourite_pl_0 = Column(String(32), nullable=True)
4848
favourite_pl_1 = Column(String(32), nullable=True)
4949
biography = Column(Text, nullable=True)
50-
photo_url = Column(Text, nullable=True) # some urls get big, best to let it be a string
50+
photo_url = Column(Text, nullable=True) # some urls get big, best to let it be a string
5151

5252
def serializable_dict(self) -> dict:
5353
return {

src/officers/urls.py

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030

3131
async def has_officer_private_info_access(
3232
request: Request,
33-
db_session: database.DBSession,
33+
db_session: database.DBSession
3434
) -> tuple[None | str, None | str, bool]:
35-
# determine if the user has access to this private data
35+
"""determine if the user has access to private officer info"""
3636
session_id = request.cookies.get("session_id", None)
3737
if session_id is None:
3838
return None, None, False
@@ -44,6 +44,21 @@ async def has_officer_private_info_access(
4444
has_private_access = await OfficerPrivateInfo.has_permission(db_session, computing_id)
4545
return session_id, computing_id, has_private_access
4646

47+
async def logged_in_or_raise(
48+
request: Request,
49+
db_session: database.DBSession
50+
) -> tuple[str, str]:
51+
"""gets the user's computing_id, or raises an exception if the current request is not logged in"""
52+
session_id = request.cookies.get("session_id", None)
53+
if session_id is None:
54+
raise HTTPException(status_code=401)
55+
56+
session_computing_id = await auth.crud.get_computing_id(db_session, session_id)
57+
if session_computing_id is None:
58+
raise HTTPException(status_code=401)
59+
60+
return session_id, session_computing_id
61+
4762
# ---------------------------------------- #
4863
# endpoints
4964

@@ -58,62 +73,60 @@ async def current_officers(
5873
):
5974
_, _, has_private_access = await has_officer_private_info_access(request, db_session)
6075
current_officers = await officers.crud.current_officers(db_session, has_private_access)
61-
json_current_executives = {
76+
return JSONResponse({
6277
position: [
63-
officer_data.serializable_dict() for officer_data in officer_data_list
64-
] for position, officer_data_list in current_officers.items()
65-
}
66-
67-
return JSONResponse(json_current_executives)
78+
officer_data.serializable_dict()
79+
for officer_data in officer_data_list
80+
]
81+
for position, officer_data_list in current_officers.items()
82+
})
6883

6984
@router.get(
7085
"/all",
71-
description="Information for all execs from all exec terms",
86+
description="Information for all execs from all exec terms"
7287
)
7388
async def all_officers(
7489
request: Request,
7590
db_session: database.DBSession,
7691
# Officer terms for officers which have not yet started their term yet are considered private,
77-
# and may only be accessed by that officer and executives.
78-
view_not_started_officer_terms: bool = False,
92+
# and may only be accessed by that officer and executives. All other officer terms are public.
93+
include_future_terms: bool = False,
7994
):
8095
_, computing_id, has_private_access = await has_officer_private_info_access(request, db_session)
81-
if view_not_started_officer_terms:
96+
if include_future_terms:
8297
is_website_admin = (computing_id is not None) and (await WebsiteAdmin.has_permission(db_session, computing_id))
8398
if not is_website_admin:
8499
raise HTTPException(status_code=401, detail="only website admins can view all executive terms that have not started yet")
85100

86-
all_officers = await officers.crud.all_officers(db_session, has_private_access, not view_not_started_officer_terms)
101+
all_officers = await officers.crud.all_officers(db_session, has_private_access, include_future_terms)
87102
return JSONResponse([
88103
officer_data.serializable_dict()
89104
for officer_data in all_officers
90105
])
91106

92107
@router.get(
93108
"/terms/{computing_id}",
94-
description="Get term info for an executive. Private info will be provided if you have permissions.",
109+
description="""Get term info for an executive. All term info is public for all past or active terms.""",
110+
tags=["login-required"]
95111
)
96112
async def get_officer_terms(
97113
request: Request,
98114
db_session: database.DBSession,
99115
computing_id: str,
116+
# TODO: remove `max_terms`
100117
# the maximum number of terms to return, in chronological order
118+
# helpful if you only want the most recent... though a person can have many active terms,
119+
# so not really that helpful imo....
101120
max_terms: int | None = None,
102-
include_inactive: bool = False,
121+
include_future_terms: bool = False
103122
):
104-
# TODO: put these into a function
105-
session_id = request.cookies.get("session_id", None)
106-
if session_id is None:
107-
raise HTTPException(status_code=401)
108-
109-
# TODO: put these into a function
110-
session_computing_id = await auth.crud.get_computing_id(db_session, session_id)
111-
if session_computing_id is None:
112-
raise HTTPException(status_code=401)
123+
# TODO: should this be login-required if a user does not want to include future terms? The info is
124+
# supposed to all be public
125+
_, session_computing_id = await logged_in_or_raise(request, db_session)
113126

114127
if (
115128
computing_id != session_computing_id
116-
and include_inactive
129+
and include_future_terms
117130
and not await WebsiteAdmin.has_permission(db_session, session_computing_id)
118131
):
119132
raise HTTPException(status_code=401)
@@ -123,37 +136,33 @@ async def get_officer_terms(
123136
db_session,
124137
computing_id,
125138
max_terms,
126-
include_inactive=include_inactive
139+
include_future_terms
127140
)
128-
return JSONResponse([term.serializable_dict() for term in officer_terms])
141+
return JSONResponse([
142+
term.serializable_dict() for term in officer_terms
143+
])
129144

130145
@router.get(
131146
"/info/{computing_id}",
132-
description="Get officer info for the current user, if they've ever been an exec.",
147+
description="Get officer info for the current user, if they've ever been an exec. Only admins can get info about another user.",
148+
tags=["login-required"]
133149
)
134150
async def get_officer_info(
135151
request: Request,
136152
db_session: database.DBSession,
137153
computing_id: str,
138154
):
139-
session_id = request.cookies.get("session_id", None)
140-
if session_id is None:
141-
raise HTTPException(status_code=401)
142-
143-
session_computing_id = await auth.crud.get_computing_id(db_session, session_id)
144-
if session_computing_id is None:
145-
raise HTTPException(status_code=401)
155+
_, session_computing_id = await logged_in_or_raise(request, db_session)
146156

147-
session_computing_id = await auth.crud.get_computing_id(db_session, session_id)
148157
if (
149158
computing_id != session_computing_id
150159
and not await WebsiteAdmin.has_permission(db_session, session_computing_id)
151160
):
152-
# the current user can only input the info for another user if they have permissions
153161
raise HTTPException(status_code=401, detail="must have website admin permissions to get officer info about another user")
154162

155-
officer_info = await officers.crud.officer_info(db_session, computing_id)
163+
officer_info = await officers.crud.get_officer_info(db_session, computing_id)
156164
if officer_info is None:
165+
# this will be triggered if a non-officer calls the endpoint
157166
raise HTTPException(status_code=404, detail="user has no officer info")
158167

159168
return JSONResponse(officer_info.serializable_dict())
@@ -210,7 +219,7 @@ async def new_officer_term(
210219
"After elections, officer computing ids are input into our system. "
211220
"If you have been elected as a new officer, you may authenticate with SFU CAS, "
212221
"then input your information & the valid token for us. Admins may update this info."
213-
),
222+
)
214223
)
215224
async def update_info(
216225
request: Request,
@@ -236,7 +245,7 @@ async def update_info(
236245

237246
# TODO: log all important changes just to a .log file
238247

239-
old_officer_info = await officers.crud.officer_info(db_session, computing_id)
248+
old_officer_info = await officers.crud.get_officer_info(db_session, computing_id)
240249
new_officer_info = officer_info_upload.to_officer_info(
241250
computing_id=computing_id,
242251
discord_id=None,
@@ -295,14 +304,15 @@ async def update_info(
295304

296305
await db_session.commit()
297306

298-
updated_officer_info = await officers.crud.officer_info(db_session, computing_id)
307+
updated_officer_info = await officers.crud.get_officer_info(db_session, computing_id)
299308
return JSONResponse({
300309
"updated_officer_info": updated_officer_info.serializable_dict(),
301310
"validation_failures": validation_failures,
302311
})
303312

304313
@router.patch(
305314
"/term/{term_id}",
315+
description=""
306316
)
307317
async def update_term(
308318
request: Request,
@@ -321,7 +331,7 @@ async def update_term(
321331
if session_computing_id is None:
322332
raise HTTPException(status_code=401)
323333

324-
old_officer_term = await officers.crud.officer_term(db_session, term_id)
334+
old_officer_term = await officers.crud.get_officer_term_by_id(db_session, term_id)
325335

326336
if (
327337
old_officer_term.computing_id != session_computing_id
@@ -354,17 +364,16 @@ async def update_term(
354364

355365
await db_session.commit()
356366

357-
new_officer_term = await officers.crud.officer_term(db_session, term_id)
367+
new_officer_term = await officers.crud.get_officer_term_by_id(db_session, term_id)
358368
return JSONResponse({
359369
"updated_officer_term": new_officer_term.serializable_dict(),
360370
"validation_failures": [], # none for now, but may be important later
361371
})
362372

363-
"""
364373
@router.post(
365-
"/remove",
366-
description="Only the sysadmin, president, or DoA can submit this request. It will usually be the DoA. Removes the officer from the system entirely. BE CAREFUL WITH THIS OPTION aaaaaaaaaaaaaaaaaa.",
374+
"/remove/{term_id}",
375+
description="Remove the specified officer term. Only website admins can run this endpoint. BE CAREFUL WITH THIS!"
367376
)
368377
async def remove_officer():
378+
# TODO: this
369379
return {}
370-
"""

src/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def is_iso_format(date_str: str) -> bool:
1919
def is_active_officer(query: Select) -> Select:
2020
"""
2121
An active officer is one who is currently part of the CSSS officer team.
22-
That is, they are not tentative, or past.
22+
That is, they are not upcoming, or in the past.
2323
"""
2424
query = query.where(
2525
and_(
@@ -47,7 +47,7 @@ def is_active_term(term: OfficerTerm) -> bool:
4747
)
4848
)
4949

50-
def has_term_started(term: OfficerTerm) -> bool:
50+
def has_started_term(term: OfficerTerm) -> bool:
5151
return term.start_date <= datetime.today()
5252

5353
def is_past_term(term: OfficerTerm) -> bool:

0 commit comments

Comments
 (0)