Skip to content

Commit d54ec21

Browse files
committed
test all endpoints & fix bugs
1 parent 551e73f commit d54ec21

File tree

5 files changed

+104
-89
lines changed

5 files changed

+104
-89
lines changed

src/elections/urls.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
def _slugify(text: str) -> str:
2323
"""Creates a unique slug based on text passed in. Assumes non-unicode text."""
24-
return re.sub(r"[\W_]+", "-", text.replace("/", "").replace("&", ""))
24+
return re.sub(r"[\W_]+", "-", text.strip().replace("/", "").replace("&", ""))
2525

2626
async def _validate_user(
2727
request: Request,
@@ -80,7 +80,10 @@ async def create_election(
8080
survey_link: str | None,
8181
):
8282
if election_type not in election_types:
83-
raise RequestValidationError()
83+
raise HTTPException(
84+
status_code=status.HTTP_400_BAD_REQUEST,
85+
detail=f"unknown election type {election_type}",
86+
)
8487

8588
is_valid_user, _, _ = await _validate_user(request, db_session)
8689
if not is_valid_user:
@@ -90,12 +93,12 @@ async def create_election(
9093
# TODO: is this header actually required?
9194
headers={"WWW-Authenticate": "Basic"},
9295
)
93-
elif len(name) <= elections.tables.MAX_ELECTION_NAME:
96+
elif len(name) > elections.tables.MAX_ELECTION_NAME:
9497
raise HTTPException(
9598
status_code=status.HTTP_400_BAD_REQUEST,
9699
detail=f"election name {name} is too long",
97100
)
98-
elif len(_slugify(name)) <= elections.tables.MAX_SLUG_NAME:
101+
elif len(_slugify(name)) > elections.tables.MAX_ELECTION_SLUG:
99102
raise HTTPException(
100103
status_code=status.HTTP_400_BAD_REQUEST,
101104
detail=f"election slug {_slugify(name)} is too long",
@@ -115,9 +118,8 @@ async def create_election(
115118
detail="dates must be in order from earliest to latest",
116119
)
117120

118-
# TODO: force dates to be in order; here & on the update election endpoint
119-
120121
await elections.crud.create_election(
122+
db_session,
121123
Election(
122124
slug = _slugify(name),
123125
name = name,
@@ -126,8 +128,7 @@ async def create_election(
126128
datetime_start_voting = datetime_start_voting,
127129
datetime_end_voting = datetime_end_voting,
128130
survey_link = survey_link
129-
),
130-
db_session
131+
)
131132
)
132133
await db_session.commit()
133134

@@ -211,11 +212,11 @@ async def delete_election(
211212
headers={"WWW-Authenticate": "Basic"},
212213
)
213214

214-
await elections.crud.delete_election(_slugify(name), db_session)
215+
await elections.crud.delete_election(db_session, _slugify(name))
215216
await db_session.commit()
216217

217218
old_election = await elections.crud.get_election(db_session, _slugify(name))
218-
return JSONResponse({"exists": old_election is None})
219+
return JSONResponse({"exists": old_election is not None})
219220

220221
# registration ------------------------------------------------------------- #
221222

src/main.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import os
23

34
from fastapi import FastAPI, Request, status
45
from fastapi.encoders import jsonable_encoder
@@ -14,7 +15,20 @@
1415
logging.basicConfig(level=logging.DEBUG)
1516
database.setup_database()
1617

17-
app = FastAPI(lifespan=database.lifespan, title="CSSS Site Backend", root_path="/api")
18+
_login_link = (
19+
"https://cas.sfu.ca/cas/login?service=" + (
20+
"http%3A%2F%2Flocalhost%3A8080"
21+
if os.environ.get("LOCAL") == "true"
22+
else "https%3A%2F%2Fnew.sfucsss.org"
23+
) + "%2Fapi%2Fauth%2Flogin%3Fredirect_path%3D%2Fapi%2Fapi%2Fdocs%2F%26redirect_fragment%3D"
24+
)
25+
26+
app = FastAPI(
27+
lifespan=database.lifespan,
28+
title="CSSS Site Backend",
29+
description=f'To login, please click <a href="{_login_link}">here</a><br><br>To logout from CAS click <a href="https://cas.sfu.ca/cas/logout">here</a>',
30+
root_path="/api"
31+
)
1832

1933
app.include_router(auth.urls.router)
2034
app.include_router(elections.urls.router)

src/permission/types.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ async def has_permission(db_session: database.DBSession, computing_id: str) -> b
4040
officer_terms = await officers.crud.current_officers(db_session, True)
4141
current_election_officer = officer_terms.get(
4242
officers.constants.OfficerPosition.ELECTIONS_OFFICER
43-
)[0]
43+
)
4444
if current_election_officer is not None:
45-
# no need to verify if position is election officer, we do so above
46-
if (
47-
current_election_officer.private_data.computing_id == computing_id
48-
and current_election_officer.is_current_officer
49-
):
50-
return True
45+
for election_officer in current_election_officer[1]:
46+
if (
47+
election_officer.private_data.computing_id == computing_id
48+
and election_officer.is_current_officer
49+
):
50+
return True
5151

5252
return False
5353

src/utils.py

Lines changed: 0 additions & 71 deletions
This file was deleted.

src/utils/__init__.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import re
2+
from datetime import date, datetime
3+
4+
from sqlalchemy import Select
5+
6+
# we can't use and/or in sql expressions, so we must use these functions
7+
from sqlalchemy.sql.expression import and_, or_
8+
9+
from officers.tables import OfficerTerm
10+
11+
12+
def is_iso_format(date_str: str) -> bool:
13+
try:
14+
datetime.fromisoformat(date_str)
15+
return True
16+
except ValueError:
17+
return False
18+
19+
def is_active_officer(query: Select) -> Select:
20+
"""
21+
An active officer is one who is currently part of the CSSS officer team.
22+
That is, they are not upcoming, or in the past.
23+
"""
24+
return query.where(
25+
and_(
26+
# cannot be an officer who has not started yet
27+
OfficerTerm.start_date <= date.today(),
28+
or_(
29+
# executives without a specified end_date are considered active
30+
OfficerTerm.end_date.is_(None),
31+
# check that today's timestamp is before (smaller than) the term's end date
32+
date.today() <= OfficerTerm.end_date,
33+
)
34+
)
35+
)
36+
37+
def has_started_term(query: Select) -> bool:
38+
return query.where(
39+
OfficerTerm.start_date <= date.today()
40+
)
41+
42+
def is_active_term(term: OfficerTerm) -> bool:
43+
return (
44+
# cannot be an officer who has not started yet
45+
term.start_date <= date.today()
46+
and (
47+
# executives without a specified end_date are considered active
48+
term.end_date is None
49+
# check that today's timestamp is before (smaller than) the term's end date
50+
or date.today() <= term.end_date
51+
)
52+
)
53+
54+
def is_past_term(term: OfficerTerm) -> bool:
55+
"""Any term which has concluded"""
56+
return (
57+
# an officer with no end date is current
58+
term.end_date is not None
59+
# if today is past the end date, it's a past term
60+
and date.today() > term.end_date
61+
)
62+
63+
def is_valid_phone_number(phone_number: str) -> bool:
64+
return (
65+
len(phone_number) == 10
66+
and phone_number.isnumeric()
67+
)
68+
69+
def is_valid_email(email: str):
70+
return re.match(r"^[^@]+@[^@]+\.[a-zA-Z]*$", email)
71+

0 commit comments

Comments
 (0)