Skip to content

Commit 30daf2c

Browse files
authored
Merge pull request #29 from IEEECS-VIT/aryan
roundwise answer submission and dashboard
2 parents a1482ac + f1f2c5b commit 30daf2c

File tree

6 files changed

+135
-46
lines changed

6 files changed

+135
-46
lines changed

config.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@ def initialize():
1212
aws_access_key_id=os.getenv("AWS_ACCESS_KEY"),
1313
aws_secret_access_key=os.getenv("AWS_SECRET_KEY")
1414
)
15+
16+
domain_tables = {
17+
"ai": dynamodb.Table("domain-ai"),
18+
"app": dynamodb.Table("domain-app"),
19+
"events": dynamodb.Table("domain-events"),
20+
"graphic": dynamodb.Table("domain-graphic"),
21+
"iot": dynamodb.Table("domain-iot"),
22+
"pnm": dynamodb.Table("domain-pnm"),
23+
"rnd": dynamodb.Table("domain-rnd"),
24+
"ui": dynamodb.Table("domain-ui"),
25+
"video": dynamodb.Table("domain-video"),
26+
"web": dynamodb.Table("domain-web")
27+
}
28+
1529
user_table = dynamodb.Table("enrollments-site-users")
1630
quiz_table = dynamodb.Table("enrollments-site-quiz")
1731
interview_table = dynamodb.Table("enrollments-site-interview")
@@ -26,15 +40,14 @@ def initialize():
2640
'firebase_app': firebase_app,
2741
'user_table': user_table,
2842
'quiz_table': quiz_table,
29-
'interview_table': interview_table
43+
'interview_table': interview_table,
44+
'domain_tables': domain_tables
3045
}
3146

32-
resources = initialize()
33-
34-
firebase_app = resources['firebase_app']
35-
user_table = resources['user_table']
36-
quiz_table = resources['quiz_table']
37-
interview_table = resources['interview_table']
47+
resources = None
3848

3949
def get_resources():
50+
global resources
51+
if resources is None:
52+
resources = initialize()
4053
return resources

main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from routes.user import user
55
from routes.answer import ans_app
66
from routes.slots import slot_app
7-
from routes.quiz_progress import quiz_app
7+
#from routes.quiz_progress import quiz_app
88
from routes.quiz_status import quiz_status
99
from config import initialize
1010

@@ -34,7 +34,7 @@
3434
app.mount("/domain", domain_app)
3535
app.mount("/answer", ans_app)
3636
app.mount("/slots", slot_app)
37-
app.mount("/quiz", quiz_app)
37+
#app.mount("/quiz", quiz_app)
3838
app.mount("/quiz-status", quiz_status)
3939
@app.get("/ping")
4040
async def ping():

routes/answer.py

Lines changed: 74 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from middleware.verifyToken import get_access_token
55
from firebase_admin import auth
66
from config import initialize
7+
from fastapi.responses import JSONResponse
78
from botocore.exceptions import ClientError
89

910
ans_app = FastAPI()
@@ -14,63 +15,103 @@
1415

1516

1617
class AnswerStruct(BaseModel):
17-
domain: str = Field(..., title="Domain for the answers")
18-
questions: List[str] = Field(..., title="List of questions")
19-
answers: List[str] = Field(..., title="List of answers")
20-
score: Optional[int] = Field(None, title="Score for the domain, not compulsory")
18+
domain: str = Field(...)
19+
questions: List[str] = Field(...)
20+
answers: List[str] = Field(...)
21+
score: Optional[int] = Field(None)
2122
round: int
2223

24+
domain_mapping = {
25+
"UI/UX": "ui",
26+
"Graphic Design": "graphic",
27+
"Video Editing": "video",
28+
'Events':'events',
29+
'PnM':'pnm',
30+
'WEB':'web',
31+
'IOT':'iot',
32+
'APP':'app',
33+
'AI/ML':'ai',
34+
'RND':'rnd'
35+
}
36+
2337
@ans_app.post("/submit")
2438
async def post_answers(answerReq: AnswerStruct, idToken: str = Depends(get_access_token)):
2539
try:
2640
decoded_token = auth.verify_id_token(idToken, app=resources["firebase_app"])
2741
email = decoded_token.get("email")
2842

2943
if not email:
30-
raise HTTPException(status_code=401, detail="Invalid or missing email in token.")
44+
return JSONResponse(status_code=401, content="Invalid or missing email in token.")
3145

3246
response = user_table.get_item(Key={"uid": email})
3347
user = response.get("Item")
3448

3549
if not user:
36-
raise HTTPException(status_code=404, detail="User not found.")
50+
return JSONResponse(status_code=404, content="User not found.")
3751

38-
if len(answerReq.questions) != len(answerReq.answers):
39-
raise HTTPException(
40-
status_code=400,
41-
detail="Questions and answers lists must have the same length."
42-
)
52+
if answerReq.domain not in user.get('domains', []):
53+
return JSONResponse(status_code=408, content="Domain was not selected")
54+
mapped_domain = domain_mapping.get(answerReq.domain)
55+
domain_tables = resources['domain_tables']
56+
domain_table = domain_tables.get(mapped_domain)
4357

44-
existing_round = user.get(f"round {answerReq.round}", {})
58+
if not domain_table:
59+
raise HTTPException(status_code=400, detail=f"Domain '{answerReq.domain}' not recognized.")
4560

46-
if answerReq.domain in existing_round:
47-
raise HTTPException(
48-
status_code=400,
49-
detail=f"Answers for domain '{answerReq.domain}' have already been submitted for round {answerReq.round}."
50-
)
61+
if len(answerReq.questions) != len(answerReq.answers):
62+
raise HTTPException(status_code=400, detail="Questions and answers lists must have the same length.")
5163

52-
answers_dict = [
53-
{"question": q, "answer": a}
54-
for q, a in zip(answerReq.questions, answerReq.answers)
55-
]
64+
answers_dict = [{"question": q, "answer": a} for q, a in zip(answerReq.questions, answerReq.answers)]
65+
response = domain_table.get_item(Key={'email': email})
5666

57-
domain_data = {"answers": answers_dict}
58-
if answerReq.score is not None:
59-
domain_data["score"] = answerReq.score
6067

61-
existing_round[answerReq.domain] = domain_data
6268

63-
user_table.update_item(
64-
Key={"uid": email},
65-
UpdateExpression="SET #round = :updated_round",
66-
ExpressionAttributeNames={"#round": f"round {answerReq.round}"},
67-
ExpressionAttributeValues={":updated_round": existing_round},
68-
ReturnValues="UPDATED_NEW",
69-
)
69+
if answerReq.round == 1:
70+
if 'Item' in response:
71+
return JSONResponse(status_code=409, content="Answers already submitted")
72+
73+
domain_table.put_item(
74+
Item={
75+
"email": email,
76+
f"round{answerReq.round}": answers_dict,
77+
f"score{answerReq.round}": answerReq.score
78+
}
79+
)
80+
else:
81+
result = domain_table.get_item(Key={"email": email})
82+
domain_response = result.get('Item')
83+
if not domain_response or not domain_response.get(f"round{answerReq.round - 1}"):
84+
raise HTTPException(
85+
status_code=400,
86+
detail=f"User '{email}' has not completed round {answerReq.round - 1} in domain '{answerReq.domain}'."
87+
)
88+
domain_table.update_item(Key={"email": email},
89+
UpdateExpression="""
90+
SET #new_answers = if_not_exists(#new_answers, :new_answers),
91+
#new_score = if_not_exists(#new_score, :new_score)
92+
""",
93+
ExpressionAttributeNames={
94+
"#new_answers": f"round{answerReq.round}",
95+
"#new_score": f"score{answerReq.round}"
96+
},
97+
ExpressionAttributeValues={
98+
":new_answers": answers_dict,
99+
":new_score": answerReq.score
100+
},
101+
ReturnValues="UPDATED_NEW"
102+
)
70103

104+
user_table.update_item(
105+
Key={'uid': email},
106+
UpdateExpression=f"SET round{answerReq.round} = list_append(if_not_exists(round{answerReq.round}, :empty_list), :new_value)",
107+
ExpressionAttributeValues={
108+
':new_value': [answerReq.domain],
109+
':empty_list': []
110+
},
111+
)
71112
return {"message": f"Answers for domain '{answerReq.domain}' submitted successfully for round {answerReq.round}."}
72113

73114
except ClientError as e:
74115
raise HTTPException(status_code=500, detail=f"DynamoDB error: {str(e)}")
75116
except Exception as e:
76-
raise HTTPException(status_code=400, detail=f"Error posting answers: {str(e)}")
117+
raise HTTPException(status_code=400, detail=f"Error posting answers: {str(e)}")

routes/domain.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ async def post_domain(domain: Dict[str, List[str]], id_token: str = Depends(get_
3030
if len(domain_list) > 2:
3131
raise HTTPException(status_code=400, content=f"Domain array for key {key} cannot have more than 2 entries")
3232

33-
user['domain'] = domain
33+
all_domains = []
34+
for domain_list in domain.values():
35+
all_domains.extend(domain_list)
36+
37+
user['domain'] = all_domains
3438
user_table.put_item(Item=user)
3539

3640
return JSONResponse(status_code=200, content=domain)
@@ -48,7 +52,6 @@ async def get_qs(domain: str, round: str):
4852

4953
round_data = field.get(round)
5054
if not round_data:
51-
print('no')
5255
return JSONResponse(status_code=401, content=f"Round {round} Questions not found")
5356
formatted_questions = []
5457

routes/quiz_status.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from fastapi import FastAPI, HTTPException
22
from pydantic import BaseModel
3-
import boto3
43
from firebase_admin import auth
54

65
from config import initialize

routes/user.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from fastapi import FastAPI, HTTPException, Depends
1+
from fastapi import FastAPI, HTTPException, Depends, Query
22
from fastapi.responses import JSONResponse
33
from firebase_admin import auth
44
from pydantic import BaseModel
@@ -109,3 +109,36 @@ async def submit_username(
109109
raise e
110110
except Exception as e:
111111
return JSONResponse(status_code=500, content= f"Internal Server Error: {str(e)}")
112+
113+
@user.get("/dashboard")
114+
async def get_dashboard(round: int = Query(..., description="Round number"), authorization: str = Depends(get_access_token), resources: dict = Depends(get_resources)):
115+
try:
116+
if not authorization:
117+
raise HTTPException(status_code=400, detail="Authorization token missing")
118+
119+
try:
120+
decoded_token = auth.verify_id_token(authorization)
121+
except Exception as token_error:
122+
raise HTTPException(status_code=401, detail=f"Invalid token: {str(token_error)}")
123+
124+
email = decoded_token.get('email')
125+
if not email:
126+
raise HTTPException(status_code=400, detail="Email not found in ID token")
127+
128+
try:
129+
user_table = resources.get("user_table")
130+
response = user_table.get_item(Key={'uid': email})
131+
user = response.get('Item')
132+
133+
except Exception as db_error:
134+
raise HTTPException(status_code=500, detail=f"Database lookup failed: {str(db_error)}")
135+
136+
pending = list(
137+
set(map(str.lower, user.get('domains', []))) -
138+
set(map(str.lower, user.get(f'round{round}', [])))
139+
)
140+
return JSONResponse(status_code=200, content=pending)
141+
142+
except Exception as unexpected_error:
143+
raise HTTPException(status_code=500, detail=f"Unexpected server error: {str(unexpected_error)}")
144+

0 commit comments

Comments
 (0)