Skip to content

Commit 6faf55d

Browse files
Complete filtering of opportunities (#290)
2 parents 3156f8e + 66eb25d commit 6faf55d

File tree

5 files changed

+93
-110
lines changed

5 files changed

+93
-110
lines changed

labconnect/helpers.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,20 @@ def format_credits(credit_1, credit_2, credit_3, credit_4):
8787
credits_output.append("4")
8888

8989
# Handle different cases
90-
if len(credits_output) == 0:
91-
return None
90+
if len(credits_output) == 4:
91+
return "1-4 Credits"
9292
elif len(credits_output) == 1:
9393
return (
9494
f"{credits_output[0]} Credit"
9595
if credit_1
9696
else f"{credits_output[0]} Credits"
9797
)
98-
elif len(credits_output) == 4:
99-
return "1-4 Credits"
98+
elif credits_output == ["1", "2", "3"]:
99+
return "1-3 Credits"
100+
elif credits_output == ["2", "3", "4"]:
101+
return "2-4 Credits"
102+
elif len(credits_output) == 0:
103+
return None
100104
else:
101105
return f"{','.join(credits_output)} Credits"
102106

labconnect/main/opportunity_routes.py

Lines changed: 80 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from flask import abort, request
44
from flask_jwt_extended import get_jwt_identity, jwt_required
5-
from sqlalchemy import func
5+
from sqlalchemy import func, case
66

77
from labconnect import db
88
from labconnect.helpers import (
@@ -143,31 +143,6 @@ def packageIndividualOpportunity(opportunityInfo):
143143
return data
144144

145145

146-
def packageOpportunityCard(opportunity):
147-
# get professor and department by getting Leads and LabManager
148-
query = db.session.execute(
149-
db.select(Leads, LabManager, User.first_name, User.last_name)
150-
.where(Leads.opportunity_id == opportunity.id)
151-
.join(LabManager, Leads.lab_manager_id == LabManager.id)
152-
.join(User, LabManager.id == User.lab_manager_id)
153-
)
154-
155-
data = query.all()
156-
157-
professorInfo = ", ".join(item[1].getName() for item in data)
158-
159-
card = {
160-
"id": opportunity.id,
161-
"title": opportunity.name,
162-
"professor": professorInfo,
163-
"season": opportunity.semester,
164-
"location": "TBA",
165-
"year": opportunity.year,
166-
}
167-
168-
return card
169-
170-
171146
@main_blueprint.get("/getOpportunity/<int:opp_id>")
172147
def getOpportunity(opp_id: int):
173148
# query database for opportunity and recommended class years
@@ -200,86 +175,94 @@ def getOpportunity(opp_id: int):
200175

201176

202177
@main_blueprint.get("/opportunity/filter")
203-
def getOpportunities():
204-
# Handle GET requests for fetching default active opportunities
205-
data = db.session.execute(
206-
db.select(Opportunities)
207-
.where(Opportunities.active)
208-
.limit(20)
209-
.order_by(Opportunities.last_updated.desc())
210-
.distinct()
211-
).scalars()
212-
result = [serialize_opportunity(opportunity) for opportunity in data]
213-
return result
214-
215-
216-
@main_blueprint.post("/opportunity/filter")
178+
@jwt_required()
217179
def filterOpportunities():
218-
# Handle POST requests for filtering opportunities
219-
json_request_data = request.get_json()
220-
221-
if not json_request_data:
222-
abort(400)
223-
224-
filters = json_request_data.get("filters", None)
225-
180+
# Handle GET requests for filtering opportunities using query parameters
181+
filters = request.args.to_dict(flat=False)
182+
user_id = get_jwt_identity()
226183
data = None
227184

228-
if filters is None:
229-
data = db.session.execute(db.select(Opportunities).limit(20)).scalars()
230-
231-
elif not isinstance(filters, list):
232-
abort(400)
233-
234-
else:
235-
where_conditions = []
236-
query = (
237-
db.select(Opportunities)
238-
.where(Opportunities.active)
239-
.limit(20)
240-
.order_by(Opportunities.last_updated)
241-
.distinct()
185+
query = (
186+
db.select(
187+
Opportunities,
188+
func.json_agg(
189+
func.json_build_object(
190+
"first_name",
191+
User.first_name,
192+
"last_name",
193+
User.last_name,
194+
"preferred_name",
195+
User.preferred_name,
196+
)
197+
).label("lab_managers"),
198+
case((UserSavedOpportunities.user_id.isnot(None), True), else_=False).label(
199+
"is_saved"
200+
),
201+
)
202+
.join(Leads, Opportunities.id == Leads.opportunity_id)
203+
.join(LabManager, Leads.lab_manager_id == LabManager.id)
204+
.join(User, LabManager.id == User.lab_manager_id)
205+
.outerjoin(
206+
RecommendsMajors, Opportunities.id == RecommendsMajors.opportunity_id
242207
)
243-
for given_filter in filters:
244-
field = given_filter.get("field", None)
245-
value = given_filter.get("value", None)
208+
.outerjoin(
209+
UserSavedOpportunities,
210+
db.and_(
211+
Opportunities.id == UserSavedOpportunities.opportunity_id,
212+
UserSavedOpportunities.user_id == user_id, # filter for current user
213+
),
214+
)
215+
.where(Opportunities.active)
216+
.group_by(Opportunities.id, UserSavedOpportunities.user_id)
217+
.order_by(Opportunities.last_updated)
218+
.limit(20)
219+
)
246220

221+
if filters is not None or filters != {}:
222+
where_conditions = []
223+
for field, value in filters.items():
247224
if field and value:
248225
field = field.lower()
226+
value = value[0].split(",")
249227

250228
# Location filter
229+
# not in use yet
251230
if field == "location":
252-
if value.lower() == "remote":
253-
where_conditions.append(Opportunities.location == "REMOTE")
254-
else:
255-
where_conditions.append(Opportunities.location != "REMOTE")
231+
for location in value:
232+
if location.lower() == "remote":
233+
where_conditions.append(Opportunities.location == "REMOTE")
234+
else:
235+
where_conditions.append(Opportunities.location != "REMOTE")
256236

257237
# Class year filter
258-
elif field == "class_year":
238+
elif field == "years":
259239
if not isinstance(value, list):
260240
abort(400)
241+
years = list(map(int, filter(str.isdigit, value)))
242+
if len(years) == 0:
243+
abort(400)
261244
query = query.join(
262245
RecommendsClassYears,
263246
Opportunities.id == RecommendsClassYears.opportunity_id,
264-
).where(RecommendsClassYears.class_year.in_(value))
247+
).where(RecommendsClassYears.class_year.in_(years))
265248

266249
# Credits filter
267250
elif field == "credits":
268251
if not isinstance(value, list):
269252
abort(400)
270253
credit_conditions = []
271254
for credit in value:
272-
if credit == 1:
255+
if credit == "1":
273256
credit_conditions.append(Opportunities.one_credit.is_(True))
274-
elif credit == 2:
257+
elif credit == "2":
275258
credit_conditions.append(
276259
Opportunities.two_credits.is_(True)
277260
)
278-
elif credit == 3:
261+
elif credit == "3":
279262
credit_conditions.append(
280263
Opportunities.three_credits.is_(True)
281264
)
282-
elif credit == 4:
265+
elif credit == "4":
283266
credit_conditions.append(
284267
Opportunities.four_credits.is_(True)
285268
)
@@ -291,12 +274,10 @@ def filterOpportunities():
291274
elif field == "majors":
292275
if not isinstance(value, list):
293276
abort(400)
294-
query = query.join(
295-
RecommendsMajors,
296-
Opportunities.id == RecommendsMajors.opportunity_id,
297-
).where(RecommendsMajors.major_code.in_(value))
277+
where_conditions.append(RecommendsMajors.major_code.in_(value))
298278

299279
# Departments filter
280+
# not currently in use
300281
elif field == "departments":
301282
if not isinstance(value, list):
302283
abort(400)
@@ -307,16 +288,11 @@ def filterOpportunities():
307288
)
308289

309290
# Pay filter
310-
elif field == "pay":
311-
if not isinstance(value, dict):
291+
elif field == "hourlypay":
292+
pay = value[0]
293+
if not pay.isnumeric():
312294
abort(400)
313-
min_pay = value.get("min")
314-
max_pay = value.get("max")
315-
if min_pay is None:
316-
min_pay = 0
317-
if max_pay is None:
318-
max_pay = float("inf")
319-
where_conditions.append(Opportunities.pay.between(min_pay, max_pay))
295+
where_conditions.append(Opportunities.pay >= float(pay))
320296

321297
# Other fields
322298
else:
@@ -328,29 +304,29 @@ def filterOpportunities():
328304
abort(400)
329305

330306
query = query.where(*where_conditions)
331-
data = db.session.execute(query).scalars()
307+
308+
data = db.session.execute(query).all()
332309

333310
if not data:
334311
abort(404)
335312

336-
result = [serialize_opportunity(opportunity) for opportunity in data]
313+
result = [
314+
serialize_opportunity(
315+
opportunity[0],
316+
lab_managers=", ".join(
317+
[
318+
f"{name.get('preferred_name', None) or name.get('first_name')} {name.get('last_name')}"
319+
for name in opportunity[1]
320+
]
321+
),
322+
saved=opportunity[2],
323+
)
324+
for opportunity in data
325+
]
337326

338327
return result
339328

340329

341-
# Jobs page
342-
@main_blueprint.get("/getOpportunityCards")
343-
def getOpportunityCards():
344-
# query database for opportunity
345-
query = db.session.execute(db.select(Opportunities).where(Opportunities.active))
346-
347-
data = query.fetchall()
348-
# return data in the below format if opportunity is found
349-
cards = {"data": [packageOpportunityCard(opportunity[0]) for opportunity in data]}
350-
351-
return cards
352-
353-
354330
@main_blueprint.get("/staff/opportunities/<string:rcs_id>")
355331
def getLabManagerOpportunityCards(rcs_id: str) -> list[dict[str, str]]:
356332
query = (
@@ -388,6 +364,7 @@ def getLabManagerOpportunityCards(rcs_id: str) -> list[dict[str, str]]:
388364

389365

390366
@main_blueprint.get("/profile/opportunities/<string:rcs_id>")
367+
@jwt_required()
391368
def getProfileOpportunities(rcs_id: str) -> list[dict[str, str]]:
392369
query = (
393370
db.select(

labconnect/main/routes.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# from typing import Any
2-
31
from typing import NoReturn
42
from flask import abort, request
53
from flask_jwt_extended import get_jwt_identity, jwt_required

labconnect/serializers.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ def serialize_course(course: Courses) -> str:
66
return f"{course.code} {course.name}"
77

88

9-
def serialize_opportunity(opportunity: Opportunities) -> dict:
9+
def serialize_opportunity(
10+
opportunity: Opportunities, lab_managers: str = "", saved: bool = False
11+
) -> dict:
1012
return {
1113
"id": opportunity.id,
1214
"name": opportunity.name,
@@ -25,4 +27,6 @@ def serialize_opportunity(opportunity: Opportunities) -> dict:
2527
"active": opportunity.active,
2628
"last_updated": opportunity.last_updated,
2729
"location": opportunity.location,
30+
"lab_managers": lab_managers,
31+
"saved": saved,
2832
}

misc/LabConnect_Logo.png

3.53 KB
Loading

0 commit comments

Comments
 (0)