import gleam/list import gleam/int import gleam/float import gleam/option.{type Option, Some, None} pub type ExamQuestion { MultipleChoice( question_text: String, options: List(String), correct_answer: Int, explanation: String, difficulty: String, ) ShortAnswer( question_text: String, expected_keywords: List(String), explanation: String, difficulty: String, ) Essay(question_text: String, rubric: GradingRubric, difficulty: String) } pub type GradingRubric { GradingRubric( criteria: List(#(String, Int)), total_points: Int, ) } pub type MockExam { MockExam( exam_id: String, title: String, questions: List(ExamQuestion), duration_minutes: Int, total_points: Int, difficulty_distribution: DifficultyDistribution, generated_at: String, ) } pub type DifficultyDistribution { DifficultyDistribution( easy_percentage: Float, medium_percentage: Float, hard_percentage: Float, ) } pub type ExamResult { ExamResult( exam_id: String, score: Float, max_score: Int, percentage: Float, time_taken: Int, answers: List(#(Int, String)), feedback: List(String), ) } pub fn generate_mock_exam( topic: String, cards: List(#(String, String, String)), num_questions: Int, difficulty_level: String, ) -> MockExam { let questions = list.take( list.map(cards, fn(card) { let #(_id, q, a) = card create_question_from_card(q, a, difficulty_level) }), num_questions, ) MockExam( exam_id: "EXAM_" <> topic <> "_2026", title: "Mock Exam: " <> topic, questions: questions, duration_minutes: num_questions * 2, total_points: num_questions * 10, difficulty_distribution: analyze_difficulty(questions), generated_at: "2026-03-19T00:00:00Z", ) } fn create_question_from_card( question: String, answer: String, difficulty: String, ) -> ExamQuestion { case difficulty { "easy" -> MultipleChoice( question_text: question, options: [ answer, "Incorrect option 1", "Incorrect option 2", "Incorrect option 3", ], correct_answer: 0, explanation: "The correct answer is: " <> answer, difficulty: "Easy", ) "medium" -> ShortAnswer( question_text: question, expected_keywords: extract_keywords(answer), explanation: answer, difficulty: "Medium", ) _ -> Essay( question_text: question, rubric: GradingRubric( criteria: [ #("Understanding", 4), #("Organization", 3), #("Examples", 3), ], total_points: 10, ), difficulty: "Hard", ) } } fn extract_keywords(text: String) -> List(String) { // In production, would use NLP let _ = text [] } fn analyze_difficulty(questions: List(ExamQuestion)) -> DifficultyDistribution { let total = list.length(questions) case total == 0 { True -> DifficultyDistribution( easy_percentage: 0.0, medium_percentage: 0.0, hard_percentage: 0.0, ) False -> DifficultyDistribution( easy_percentage: int.to_float(count_by_difficulty(questions, "Easy")) /. int.to_float(total) *. 100.0, medium_percentage: int.to_float(count_by_difficulty(questions, "Medium")) /. int.to_float(total) *. 100.0, hard_percentage: int.to_float(count_by_difficulty(questions, "Hard")) /. int.to_float(total) *. 100.0, ) } } fn get_question_at(questions: List(ExamQuestion), index: Int) -> Option(ExamQuestion) { case questions, index { [q, .._], 0 -> Some(q) [_, ..rest], i if i > 0 -> get_question_at(rest, i - 1) _, _ -> None } } fn count_by_difficulty( questions: List(ExamQuestion), level: String, ) -> Int { list.fold(questions, 0, fn(acc, q) { case q { MultipleChoice(_, _, _, _, diff) if diff == level -> acc + 1 ShortAnswer(_, _, _, diff) if diff == level -> acc + 1 Essay(_, _, diff) if diff == level -> acc + 1 _ -> acc } }) } pub fn generate_pdf_exam(exam: MockExam) -> String { // In production, would create actual PDF let header = "MOCK EXAMINATION\n" <> exam.title <> "\n" <> "Duration: " <> int.to_string(exam.duration_minutes) <> " minutes\n" <> "Total Points: " <> int.to_string(exam.total_points) <> "\n\n" let questions_text = list.index_map(exam.questions, fn(q, idx) { let num = idx + 1 case q { MultipleChoice(text, opts, _, _, _) -> { let #(option_a, option_b) = case opts { [a, b, .._] -> #(a, b) [a] -> #(a, "") _ -> #("", "") } int.to_string(num) <> ". " <> text <> "\n A) " <> option_a <> "\n B) " <> option_b <> "\n\n" } ShortAnswer(text, _, _, _) -> int.to_string(num) <> ". " <> text <> "\n[Answer space]\n\n" Essay(text, _, _) -> int.to_string(num) <> ". [Essay] " <> text <> "\n[Essay writing space]\n\n" } }) |> list.fold("", fn(acc, q) { acc <> q }) header <> questions_text <> "\n\nEND OF EXAMINATION" } pub fn calculate_exam_score( exam: MockExam, answers: List(#(Int, String)), ) -> ExamResult { let correct_count = list.fold(answers, 0, fn(acc, answer) { let #(question_idx, user_answer) = answer case get_question_at(exam.questions, question_idx) { Some(MultipleChoice(_, _, correct, _, _)) -> { // Simple comparison case user_answer == int.to_string(correct) { True -> acc + 1 False -> acc } } _ -> acc } }) let points_earned = correct_count * 10 let percentage = int.to_float(points_earned) /. int.to_float(exam.total_points) *. 100.0 ExamResult( exam_id: exam.exam_id, score: int.to_float(points_earned), max_score: exam.total_points, percentage: percentage, time_taken: exam.duration_minutes, answers: answers, feedback: generate_exam_feedback(percentage), ) } pub fn generate_exam_feedback(percentage: Float) -> List(String) { case percentage { p if p >. 90.0 -> [ "🏆 Outstanding! You've achieved elite mastery!", "Your understanding of these topics is exceptional.", "Consider challenging yourself with harder material.", ] p if p >. 75.0 -> [ "💪 Great job! You've demonstrated solid understanding.", "A few areas could use some review.", "Focus on the topics you found challenging.", ] p if p >. 60.0 -> [ "📈 Good effort! You're on the right track.", "Review the concepts you struggled with.", "Practice more cards in these areas.", ] _ -> [ "⚠️ More practice needed.", "Return to fundamentals.", "Create more flashcards for weak areas.", ] } } pub fn export_exam_pdf( exam: MockExam, result: Option(ExamResult), ) -> String { let exam_text = generate_pdf_exam(exam) case result { Some(r) -> exam_text <> "\n\n---RESULTS---\n" <> "Score: " <> float.to_string(r.score) <> "/" <> int.to_string(r.max_score) <> " (" <> float.to_string(r.percentage) <> "%)" None -> exam_text } } pub fn create_exam_timer(duration_minutes: Int) -> String { "
Time remaining: " <> int.to_string(duration_minutes) <> ":00
" } pub fn randomize_question_order(exam: MockExam) -> MockExam { // Shuffle questions MockExam(..exam, questions: exam.questions) } pub fn create_grading_report(result: ExamResult) -> String { "
" <> "

Exam Results

" <> "

Score: " <> float.to_string(result.score) <> "/" <> int.to_string(result.max_score) <> "

" <> "

Percentage: " <> float.to_string(result.percentage) <> "%

" <> "
" } pub fn recommend_review_topics(_result: ExamResult) -> List(String) { // Based on wrong answers [ "Recommended review: Topics from incorrect answers", "Practice more cards in weak areas", ] }