Skip to content

generate question types for each lo and manually choose question type… - #91

Open
graceQUE03 wants to merge 6 commits into
mainfrom
add-question-type-label
Open

generate question types for each lo and manually choose question type…#91
graceQUE03 wants to merge 6 commits into
mainfrom
add-question-type-label

Conversation

@graceQUE03

@graceQUE03 graceQUE03 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Bloom-level question type selection

Lets instructors control, per Bloom's Taxonomy level on a granular learning objective, which question type(s) get generated and how many of each, instead of the previous single "total question count" per objective with the type resolved implicitly from a course-wide Bloom→type preference.

Summary

On the Question Generation → Create Objectives step, each granular objective's Bloom chips (Remember, Understand, Apply, Analyze, Evaluate, Create) now expand into a per-type breakdown when clicked: Multiple Choice, Fill-in-the-Blank, Calculation, Open-Ended, each with its own -/count/+ stepper. The AI objective-generation pipeline also proposes an initial breakdown per Bloom level, which the instructor can review and adjust before generating questions.

UI changes

  • client/src/pages/question-generation/BloomTypePanel.jsx (new): the per-Bloom-level type breakdown panel. Renders the four question types horizontally, each with its own count stepper (max 3 per type).
  • ObjectiveGroupCard.jsx: clicking a selected Bloom chip now opens/ closes its BloomTypePanel instead of deselecting the level. Multiple panels can be open at once, stacking in the order they were opened. A small × on the chip removes the level (replacing the old click-to-deselect). Once an item has any per-type counts, its total "Questions" count becomes a derived, read-only badge instead of the old manual stepper.
  • ObjectivesStep.jsx: new removeBloomChip (level removal, prunes its type counts) and changeTypeCount (per bloomLevel/questionType count, recomputes the item's derived total) state functions. Loads/saves the new questionTypes field alongside bloomTaxonomies wherever granular objectives are read from or written to the API.
  • QuestionGeneration.jsx: validateStep1 now also blocks advancing if a selected Bloom level has a type breakdown with a zero total.
  • AIGenerateModal.jsx: passes the AI's suggested questionTypes through when saving a generated objective.

Backward compatible by default: granular objectives with no `questionTypes (anything created before this change) keep rendering with the original single "Questions" stepper.

Data model

New field questionTypes on a granular objective, added alongside the existing bloomTaxonomies array (not replacing it) — a flat array of { bloomLevel, questionType, count } triples:

{
  "text": "Explain the role of enzymes in catalysis.",
  "bloomTaxonomies": ["Understand", "Apply"],
  "questionTypes": [
    { "bloomLevel": "Understand", "questionType": "multiple-choice", "count": 2 },
    { "bloomLevel": "Apply", "questionType": "calculation", "count": 1 }
  ]
}

A flat array (rather than an object keyed by Bloom level) was chosen because it maps directly onto JSON Schema structured output and is trivial to validate/group client-side.

Backend changes

  • src/constants/llm-schemas.js: OBJECTIVES_SCHEMA gains questionTypes on each granular objective (enum-constrained bloomLevel and questionType, count 1–3).
  • src/constants/app-constants.js — both objective-generation prompts (OBJECTIVE_GENERATION_AUTO_PROMPT, OBJECTIVE_GENERATION_MANUAL_PROMPT) gained an instruction + guidance table asking the model to recommend question type(s) and counts per Bloom level, plus a matching self-check.
  • src/controllers/rag-llm.js (generateLearningObjectivesHandler): cleans/validates the LLM's questionTypes output (drops entries with an invalid bloom level or type; any Bloom level left with none gets a default type via the existing DEFAULT_BLOOM_TYPE_PREFERENCES, so the UI never shows an empty breakdown for a selected level).
  • src/services/objective.js: createObjective/updateObjective persist questionTypes on the granular objective document (Mongo, grasp_objective collection), same treatment as bloomTaxonomies.

Wiring into actual question generation

generateQuestionsWithRagHandler (POST /api/rag-llm/generate-questions-with-rag) now accepts an optional questionTypes array. When present, it expands the triples into an explicit (bloomLevel, questionType) work list, and replaces the old "round-robin Bloom levels for N questions, resolve type via course preference" logic for that request. When absent, generation falls back to the previous behavior unchanged.

client/src/pages/question-generation/generationApi.js (generateQuestions) passes a granular item's questionTypes through when present.

sample response from LLM

  "materialIsRelevant": true,
  "relevanceReason": "The provided course materials cover various topics in calculus, including sequences and series, Taylor's theorem, and Maclaurin series.",
  "objectives": [
    {
      "name": "Understand the concept of sequences and series",
      "granularObjectives": [
        { "text": "Students will be able to define and distinguish between a sequence and a series.", "bloomTaxonomies": ["Remember", "Understand"] ,
          "questionTypes": [ {"bloomLevel": "Remember", "questionType": "multiple-choice", "count": 1}, {"bloomLevel": "Understand", "questionType": "fill-in-the-blank", "count": 2} ]},
        { "text": "Students will be able to identify the terms and notation used in sequences and series.", "bloomTaxonomies": ["Remember", "Understand"] ,
          "questionTypes": [ {"bloomLevel": "Remember", "questionType": "multiple-choice", "count": 1}, {"bloomLevel": "Understand", "questionType": "fill-in-the-blank", "count": 2} ]},
        { "t...```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant