1010import elections
1111from elections .tables import Election , election_types
1212from permission .types import ElectionOfficer , WebsiteAdmin
13- from utils .urls import logged_in_or_raise
13+ from utils .urls import is_logged_in
1414
1515_logger = logging .getLogger (__name__ )
1616
2121
2222def _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 )
24+ return re .sub (r"[\W_]+" , "-" , text . replace ( "/" , "" ). replace ( "&" , "" ) )
2525
2626async def _validate_user (
2727 request : Request ,
2828 db_session : database .DBSession ,
2929) -> tuple [bool , str , str ]:
30- session_id , computing_id = logged_in_or_raise (request , db_session )
30+ logged_in , session_id , computing_id = await is_logged_in (request , db_session )
31+ if not logged_in :
32+ return False , None , None
33+
3134 has_permission = await ElectionOfficer .has_permission (db_session , computing_id )
3235 if not has_permission :
3336 has_permission = await WebsiteAdmin .has_permission (db_session , computing_id )
37+
3438 return has_permission , session_id , computing_id
3539
3640# elections ------------------------------------------------------------- #
3741
42+ @router .get (
43+ "/by_name/{name:str}" ,
44+ description = "Retrieves the election data for an election by name"
45+ )
46+ async def get_election (
47+ request : Request ,
48+ db_session : database .DBSession ,
49+ name : str ,
50+ ):
51+ election = await elections .crud .get_election (db_session , _slugify (name ))
52+ if election is None :
53+ raise HTTPException (
54+ status_code = status .HTTP_400_BAD_REQUEST ,
55+ detail = f"election with slug { _slugify (name )} does not exist"
56+ )
57+ elif datetime .now () >= election .datetime_start_voting :
58+ # after the voting period starts, all election data becomes public
59+ return JSONResponse (election .serializable_dict ())
60+
61+ is_valid_user , _ , _ = await _validate_user (request , db_session )
62+ return JSONResponse (
63+ election .serializable_dict ()
64+ if is_valid_user
65+ else election .public_details ()
66+ )
67+
3868@router .post (
3969 "/by_name/{name:str}" ,
4070 description = "Creates an election and places it in the database. Returns election json on success" ,
@@ -60,59 +90,57 @@ async def create_election(
6090 # TODO: is this header actually required?
6191 headers = {"WWW-Authenticate" : "Basic" },
6292 )
63- elif elections .crud .get_election (db_session , _slugify (name )) is not None :
93+ elif len (name ) <= elections .tables .MAX_ELECTION_NAME :
94+ raise HTTPException (
95+ status_code = status .HTTP_400_BAD_REQUEST ,
96+ detail = f"election name { name } is too long" ,
97+ )
98+ elif len (_slugify (name )) <= elections .tables .MAX_SLUG_NAME :
99+ raise HTTPException (
100+ status_code = status .HTTP_400_BAD_REQUEST ,
101+ detail = f"election slug { _slugify (name )} is too long" ,
102+ )
103+ elif await elections .crud .get_election (db_session , _slugify (name )) is not None :
64104 # don't overwrite a previous election
65105 raise HTTPException (
66106 status_code = status .HTTP_400_BAD_REQUEST ,
67107 detail = "would overwrite previous election" ,
68108 )
109+ elif not (
110+ (datetime_start_nominations <= datetime_start_voting )
111+ and (datetime_start_voting <= datetime_end_voting )
112+ ):
113+ raise HTTPException (
114+ status_code = status .HTTP_400_BAD_REQUEST ,
115+ detail = "dates must be in order from earliest to latest" ,
116+ )
117+
118+ # TODO: force dates to be in order; here & on the update election endpoint
69119
70120 await elections .crud .create_election (
71121 Election (
72- _slugify (name ),
73- name ,
74- election_type ,
75- datetime_start_nominations ,
76- datetime_start_voting ,
77- datetime_end_voting ,
78- survey_link
122+ slug = _slugify (name ),
123+ name = name ,
124+ type = election_type ,
125+ datetime_start_nominations = datetime_start_nominations ,
126+ datetime_start_voting = datetime_start_voting ,
127+ datetime_end_voting = datetime_end_voting ,
128+ survey_link = survey_link
79129 ),
80130 db_session
81131 )
82132 await db_session .commit ()
83133
84- election = elections .crud .get_election (db_session , _slugify (name ))
134+ election = await elections .crud .get_election (db_session , _slugify (name ))
85135 return JSONResponse (election .serializable_dict ())
86136
87- @router .delete (
88- "/by_name/{name:str}" ,
89- description = "Deletes an election from the database. Returns whether the election exists after deletion."
90- )
91- async def delete_election (
92- request : Request ,
93- db_session : database .DBSession ,
94- name : str
95- ):
96- is_valid_user , _ , _ = await _validate_user (request , db_session )
97- if not is_valid_user :
98- raise HTTPException (
99- status_code = status .HTTP_401_UNAUTHORIZED ,
100- detail = "must have election officer permission" ,
101- # TODO: is this header actually required?
102- headers = {"WWW-Authenticate" : "Basic" },
103- )
104-
105- await elections .crud .delete_election (_slugify (name ), db_session )
106- await db_session .commit ()
107-
108- old_election = elections .crud .get_election (db_session , _slugify (name ))
109- return JSONResponse ({"exists" : old_election is None })
110-
111137@router .patch (
112138 "/by_name/{name:str}" ,
113139 description = """
114140 Updates an election in the database.
115- Note that this don't let you to change the name of an election as it would generate a new slug!
141+
142+ Note that this doesn't let you change the name of an election, unless the new
143+ name produces the same slug.
116144
117145 Returns election json on success.
118146 """
@@ -135,15 +163,23 @@ async def update_election(
135163 detail = "must have election officer or admin permission" ,
136164 headers = {"WWW-Authenticate" : "Basic" },
137165 )
166+ elif not (
167+ (datetime_start_nominations <= datetime_start_voting )
168+ and (datetime_start_voting <= datetime_end_voting )
169+ ):
170+ raise HTTPException (
171+ status_code = status .HTTP_400_BAD_REQUEST ,
172+ detail = "dates must be in order from earliest to latest" ,
173+ )
138174
139175 new_election = Election (
140- _slugify (name ),
141- name ,
142- election_type ,
143- datetime_start_nominations ,
144- datetime_start_voting ,
145- datetime_end_voting ,
146- survey_link
176+ slug = _slugify (name ),
177+ name = name ,
178+ type = election_type ,
179+ datetime_start_nominations = datetime_start_nominations ,
180+ datetime_start_voting = datetime_start_voting ,
181+ datetime_end_voting = datetime_end_voting ,
182+ survey_link = survey_link
147183 )
148184 success = await elections .crud .update_election (db_session , new_election )
149185 if not success :
@@ -154,31 +190,32 @@ async def update_election(
154190 else :
155191 await db_session .commit ()
156192
157- election = elections .crud .get_election (db_session , _slugify (name ))
193+ election = await elections .crud .get_election (db_session , _slugify (name ))
158194 return JSONResponse (election .serializable_dict ())
159195
160- @router .get (
196+ @router .delete (
161197 "/by_name/{name:str}" ,
162- description = "Retrieves the election data for an election by name "
198+ description = "Deletes an election from the database. Returns whether the election exists after deletion. "
163199)
164- async def get_election (
200+ async def delete_election (
165201 request : Request ,
166202 db_session : database .DBSession ,
167- name : str ,
203+ name : str
168204):
169- election = elections . crud . get_election ( db_session , _slugify ( name ) )
170- if election is None :
205+ is_valid_user , _ , _ = await _validate_user ( request , db_session )
206+ if not is_valid_user :
171207 raise HTTPException (
172- status_code = status .HTTP_400_BAD_REQUEST ,
173- detail = f"election with slug { _slugify (name )} does not exist"
208+ status_code = status .HTTP_401_UNAUTHORIZED ,
209+ detail = "must have election officer permission" ,
210+ # TODO: is this header actually required?
211+ headers = {"WWW-Authenticate" : "Basic" },
174212 )
175213
176- is_valid_user , _ , _ = await _validate_user (request , db_session )
177- return JSONResponse (
178- election .serializable_dict ()
179- if is_valid_user
180- else election .public_details ()
181- )
214+ await elections .crud .delete_election (_slugify (name ), db_session )
215+ await db_session .commit ()
216+
217+ old_election = await elections .crud .get_election (db_session , _slugify (name ))
218+ return JSONResponse ({"exists" : old_election is None })
182219
183220# registration ------------------------------------------------------------- #
184221
0 commit comments