Skip to content

Commit c32eda1

Browse files
committed
Add gradeQuiz Function
Signed-off-by: Tal Jacob <taljacob2@gmail.com>
1 parent 3fe8a24 commit c32eda1

4 files changed

Lines changed: 319 additions & 4 deletions

File tree

nextstep-backend/src/controllers/quizzes_controller.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,20 @@ const getGeneratedQuizBySubject = async (req: Request, res: Response): Promise<v
3030
}
3131
};
3232

33-
export default { getQuizzesByTags, getGeneratedQuizBySubject };
33+
const getGradedQuizByAnsweredQuiz = async (req: Request, res: Response): Promise<void> => {
34+
try {
35+
const answeredQuiz = req.body;
36+
37+
if (!answeredQuiz) {
38+
res.status(400).json({ error: 'Missing required fields' });
39+
return;
40+
}
41+
42+
const gradedQuiz = await companiesService.gradeQuiz(answeredQuiz);
43+
res.json(gradedQuiz);
44+
} catch (err) {
45+
handleError(err, res);
46+
}
47+
};
48+
49+
export default { getQuizzesByTags, getGeneratedQuizBySubject, getGradedQuizByAnsweredQuiz };

nextstep-backend/src/openapi/swagger.yaml

Lines changed: 239 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,14 +1164,50 @@ paths:
11641164
properties:
11651165
subject:
11661166
type: string
1167-
description: The subject of the quiz to be generated
1167+
description: The subject of the quiz to be generated (e.g., "Java Spring Boot Microservices")
1168+
example: "Java Spring Boot Microservices"
11681169
responses:
11691170
'200':
11701171
description: A generated quiz custom-tailored for the targeted subject.
11711172
content:
11721173
application/json:
11731174
schema:
1174-
type: object # TODO:
1175+
$ref: '#/components/schemas/GeneratedQuiz'
1176+
'400':
1177+
description: Bad request - Missing required fields
1178+
content:
1179+
application/json:
1180+
schema:
1181+
type: object
1182+
properties:
1183+
error:
1184+
type: string
1185+
description: Error message indicating missing fields
1186+
'401':
1187+
description: Unauthorized
1188+
'500':
1189+
description: Internal server error
1190+
1191+
/quiz/grade:
1192+
post:
1193+
tags:
1194+
- Quizzes
1195+
summary: Grade a quiz answered by the user.
1196+
security:
1197+
- BearerAuth: []
1198+
requestBody:
1199+
required: true
1200+
content:
1201+
application/json:
1202+
schema:
1203+
$ref: '#/components/schemas/AnsweredQuiz'
1204+
responses:
1205+
'200':
1206+
description: A graded quiz with tips.
1207+
content:
1208+
application/json:
1209+
schema:
1210+
$ref: '#/components/schemas/GradedQuiz'
11751211
'400':
11761212
description: Bad request - Missing required fields
11771213
content:
@@ -1413,6 +1449,207 @@ components:
14131449
format: uri
14141450
example: "http://www.forum.com/f/..."
14151451

1452+
GeneratedQuiz:
1453+
type: object
1454+
required:
1455+
- _id
1456+
- title
1457+
- tags
1458+
- content
1459+
- job_role
1460+
- company_name_en
1461+
- company_name_he
1462+
- process_details
1463+
- question_list
1464+
- answer_list
1465+
- keywords
1466+
- interviewer_mindset
1467+
properties:
1468+
_id:
1469+
type: string
1470+
description: Unique identifier for the generated quiz (e.g., "generated_[TIMESTAMP]").
1471+
example: "generated_1701234567890"
1472+
title:
1473+
type: string
1474+
description: A concise and relevant title for the generated quiz.
1475+
example: "ראיון לתפקיד מפתח Java Fullstack בחברת טכנולוגיה מובילה"
1476+
tags:
1477+
type: array
1478+
description: Highly relevant technical and role-based tags.
1479+
items:
1480+
type: string
1481+
example: ["Java", "Spring Boot", "Microservices", "Developer", "ראיון"]
1482+
content:
1483+
type: string
1484+
description: A comprehensive narrative combining the interview process and specific questions.
1485+
example: "תהליך ראיון למפתח Java Fullstack בחברת טכנולוגיה גדולה. הראיון כלל מספר שלבים: ראיון טלפוני קצר... (full content of the generated quiz)"
1486+
job_role:
1487+
type: string
1488+
description: The inferred job role for the quiz.
1489+
example: "מפתח Java Fullstack"
1490+
company_name_en:
1491+
type: string
1492+
description: English name of the company for the quiz.
1493+
example: "Leading Tech Company"
1494+
company_name_he:
1495+
type: string
1496+
description: Hebrew name of the company for the quiz.
1497+
example: "חברת טכנולוגיה מובילה"
1498+
process_details:
1499+
type: string
1500+
description: A concise summary of a typical interview process for this role/company type.
1501+
example: "ראיון טלפוני קצר לבדיקת התאמה כללית וציפיות שכר. משימת בית קצרה..."
1502+
question_list:
1503+
type: array
1504+
description: A parsed list of individual interview questions.
1505+
items:
1506+
type: string
1507+
example: ["איך היית מתכנן מערכת מיקרו-שירותים?", "הסבר את הקשר בין Spring Boot ל-Spring Framework."]
1508+
answer_list:
1509+
type: array
1510+
description: A corresponding list of ideal or reference answers for each question.
1511+
items:
1512+
type: string
1513+
example: ["בתכנון מערכת מיקרו-שירותים גדולה, הייתי מתחיל בהגדרת גבולות הקונטקסט...", "Spring Boot הוא פרויקט מעל Spring Framework שמטרתו לפשט את תהליך הפיתוח..."]
1514+
keywords:
1515+
type: array
1516+
description: Additional relevant technical or conceptual keywords for preparation.
1517+
items:
1518+
type: string
1519+
example: ["REST API", "Scalability", "Design Patterns", "Concurrency"]
1520+
interviewer_mindset:
1521+
type: string
1522+
description: A description of the soft skills, characteristics, and professional attributes an interviewer looks for.
1523+
example: "מחפשים מועמד עם סקרנות טכנולוגית גבוהה, יכולת פתרון בעיות יצירתית, תקשורת ברורה ותמציתית, וגישה פרואקטיבית לשיתוף פעולה בצוות."
1524+
1525+
AnsweredQuiz:
1526+
type: object
1527+
required:
1528+
- _id
1529+
- title
1530+
- tags
1531+
- content
1532+
- job_role
1533+
- company_name_en
1534+
- company_name_he
1535+
- process_details
1536+
- question_list
1537+
- answer_list
1538+
- keywords
1539+
- interviewer_mindset
1540+
properties:
1541+
_id:
1542+
type: string
1543+
description: Unique identifier for the generated quiz (e.g., "generated_[TIMESTAMP]").
1544+
example: "generated_1701234567890"
1545+
title:
1546+
type: string
1547+
description: A concise and relevant title for the generated quiz.
1548+
example: "ראיון לתפקיד מפתח Java Fullstack בחברת טכנולוגיה מובילה"
1549+
tags:
1550+
type: array
1551+
description: Highly relevant technical and role-based tags.
1552+
items:
1553+
type: string
1554+
example: ["Java", "Spring Boot", "Microservices", "Developer", "ראיון"]
1555+
content:
1556+
type: string
1557+
description: A comprehensive narrative combining the interview process and specific questions.
1558+
example: "תהליך ראיון למפתח Java Fullstack בחברת טכנולוגיה גדולה. הראיון כלל מספר שלבים: ראיון טלפוני קצר... (full content of the generated quiz)"
1559+
job_role:
1560+
type: string
1561+
description: The inferred job role for the quiz.
1562+
example: "מפתח Java Fullstack"
1563+
company_name_en:
1564+
type: string
1565+
description: English name of the company for the quiz.
1566+
example: "Leading Tech Company"
1567+
company_name_he:
1568+
type: string
1569+
description: Hebrew name of the company for the quiz.
1570+
example: "חברת טכנולוגיה מובילה"
1571+
process_details:
1572+
type: string
1573+
description: A concise summary of a typical interview process for this role/company type.
1574+
example: "ראיון טלפוני קצר לבדיקת התאמה כללית וציפיות שכר. משימת בית קצרה..."
1575+
question_list:
1576+
type: array
1577+
description: A parsed list of individual interview questions.
1578+
items:
1579+
type: string
1580+
example: ["איך היית מתכנן מערכת מיקרו-שירותים?", "הסבר את הקשר בין Spring Boot ל-Spring Framework."]
1581+
answer_list:
1582+
type: array
1583+
description: A corresponding list of ideal or reference answers for each question.
1584+
items:
1585+
type: string
1586+
example: ["בתכנון מערכת מיקרו-שירותים גדולה, הייתי מתחיל בהגדרת גבולות הקונטקסט...", "Spring Boot הוא פרויקט מעל Spring Framework שמטרתו לפשט את תהליך הפיתוח..."]
1587+
user_answer_list:
1588+
type: array
1589+
description: A corresponding list of user answers for each question.
1590+
items:
1591+
type: string
1592+
example: ["בתכנון מערכת מיקרו-שירותים גדולה, הייתי מתחיל בהגדרת גבולות הקונטקסט...", "Spring Boot הוא פרויקט מעל Spring Framework שמטרתו לפשט את תהליך הפיתוח..."]
1593+
keywords:
1594+
type: array
1595+
description: Additional relevant technical or conceptual keywords for preparation.
1596+
items:
1597+
type: string
1598+
example: ["REST API", "Scalability", "Design Patterns", "Concurrency"]
1599+
interviewer_mindset:
1600+
type: string
1601+
description: A description of the soft skills, characteristics, and professional attributes an interviewer looks for.
1602+
example: "מחפשים מועמד עם סקרנות טכנולוגית גבוהה, יכולת פתרון בעיות יצירתית, תקשורת ברורה ותמציתית, וגישה פרואקטיבית לשיתוף פעולה בצוות."
1603+
1604+
GradedQuiz:
1605+
type: object
1606+
required:
1607+
- graded_answers
1608+
- final_quiz_grade
1609+
- final_summary_tip
1610+
properties:
1611+
graded_answers:
1612+
type: array
1613+
description: A list of individual questions with the user's answer, grade, and a tip for improvement.
1614+
items:
1615+
type: object
1616+
required:
1617+
- question
1618+
- user_answer
1619+
- grade
1620+
- tip
1621+
properties:
1622+
question:
1623+
type: string
1624+
description: The original question from the quiz.
1625+
example: "איך היית מתכנן מערכת מיקרו-שירותים (Microservices) גדולה שמטפלת במיליוני בקשות ביום?"
1626+
user_answer:
1627+
type: string
1628+
description: The user's provided answer to the question.
1629+
example: "אני חושב שהייתי משתמש בקאפקה וסרביס דיסקברי. זה עוזר עם סקאלינג."
1630+
grade:
1631+
type: number
1632+
format: int32
1633+
minimum: 0
1634+
maximum: 100
1635+
description: The grade for the user's answer (0-100).
1636+
example: 85
1637+
tip:
1638+
type: string
1639+
description: A professional tip for the user to improve their answer.
1640+
example: "Consider elaborating on the specific design patterns (e.g., API Gateway, Service Discovery) and communication protocols (e.g., REST, gRPC) you would use."
1641+
final_quiz_grade:
1642+
type: number
1643+
format: int32
1644+
minimum: 0
1645+
maximum: 100
1646+
description: The overall average grade for all answered questions in the quiz (0-100).
1647+
example: 78
1648+
final_summary_tip:
1649+
type: string
1650+
description: A comprehensive tip for the user to improve their overall interview performance.
1651+
example: "Overall, your technical understanding is solid. For future interviews, focus on articulating your thoughts more structured, perhaps using the STAR method for behavioral questions, and always relate your technical solutions back to business value or team collaboration, demonstrating the 'interviewer mindset' we discussed."
1652+
14161653
securitySchemes:
14171654
BearerAuth:
14181655
type: http

nextstep-backend/src/routes/quizzes_routes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ router.get('/raw', Quiz.getQuizzesByTags);
77

88
router.post('/generate', Quiz.getGeneratedQuizBySubject);
99

10+
router.post('/grade', Quiz.getGradedQuizByAnsweredQuiz);
11+
1012
export default router;

nextstep-backend/src/services/companies_service.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,4 +528,64 @@ Return ONLY the JSON, without any other text, so I could easily retrieve it.
528528
const parsed = JSON.parse(aiResponse.trim().replace("```json", "").replace("```", "")) as any;
529529

530530
return parsed;
531-
}
531+
}
532+
533+
/**
534+
* @param answeredQuiz The quiz answered by the user.
535+
* @returns Graded quiz with tips.
536+
*/
537+
export const gradeQuiz = async (answeredQuiz: any): Promise<any> => {
538+
const GRADE_QUIZ_SYSTEM_PROMPT = "You are an AI Interview Coach, an expert in real-world interviews, and skilled in providing constructive feedback and grading. Your task is to evaluate a user's answers to an interview quiz based on the original questions, your pre-generated ideal answers, and general professional knowledge. You should grade each answer from 0-100, considering that multiple correct or valid approaches may exist (or may not). Your feedback should be actionable and focused on improvement. Base yourself only on the user's answered quiz that will be given to you.";
539+
540+
const prompt = `
541+
**Original Quiz Data with User's Answers (JSON Object):**
542+
${answeredQuiz}
543+
544+
**Instructions:**
545+
Base yourself on the the user_answer_list and question_list fields.
546+
Also, base yourself on the answer_list and the interviewer_mindset fields.
547+
Grade and tip to user's answers one by one, by the following rules:
548+
549+
Grade (0-100): Assign a numerical score from 0 to 100 to the user_answer_list based on:
550+
Accuracy: Is the answer technically correct?
551+
Completeness: Does it cover the key aspects of the question?
552+
Clarity and Conciseness: Is it well-articulated and to the point?
553+
Depth: Does it demonstrate a solid understanding beyond surface-level knowledge?
554+
Professional Knowledge: Use your general professional knowledge to evaluate, not just keywords. Be open to multiple valid answers.
555+
Context: Consider the complexity implied by the question and the job_role.
556+
Generate "Tip": Provide a small, concise, and actionable tip for the user to improve that specific answer for future interviews. This tip should be professional and forward-looking. Consider:
557+
Suggesting deeper dives into specific technologies or concepts (e.g., "Explore idempotent operations in distributed systems").
558+
Improving explanation clarity or structure (e.g., "Next time, start with a clear definition before diving into examples").
559+
Relating the answer to the interviewer_mindset (e.g., "Highlighting the trade-offs involved would demonstrate critical thinking and problem-solving skills, which are valued by interviewers.").
560+
Suggesting a real-world example or scenario.
561+
562+
Summarize Results:
563+
564+
final_quiz_grade: Calculate the average of all individual grade scores to get a final overall quiz grade (0-100).
565+
final_summary_tip: Provide one overarching, professional tip for the user to improve their overall interview performance, drawing insights from their collective answers and directly referencing the interviewer_mindset field for behavioral/soft skill advice.
566+
567+
**Desired Output Format (JSON)**:
568+
\`\`\`json
569+
{
570+
"graded_answers": [
571+
{
572+
"question": "string",
573+
"user_answer": "string",
574+
"grade": "number (0-100)",
575+
"tip": "string"
576+
},
577+
// ... one object for each question answered
578+
],
579+
"final_quiz_grade": "number (0-100)",
580+
"final_summary_tip": "string"
581+
}
582+
\`\`\`
583+
584+
Return ONLY the JSON, without any other text, so I could easily retrieve it.
585+
`;
586+
587+
const aiResponse = await chatWithAI(GRADE_QUIZ_SYSTEM_PROMPT, [prompt]);
588+
const parsed = JSON.parse(aiResponse.trim().replace("```json", "").replace("```", "")) as any;
589+
590+
return parsed;
591+
}

0 commit comments

Comments
 (0)