feat(mcq): implement strict scoring mode for single-attempt failures

Add new setting 'Deduct full mark on first failure' that enforces strict scoring rules where incorrect first attempts result in zero points. This provides an alternative scoring system to the original partial credit approach, giving users flexibility in assessment strictness.

The feature includes:
- New boolean setting in plugin configuration
- Updated scoring logic in MCQ modal
- Settings UI toggle control
- TypeScript interface updates
- CSS styling for result display

This allows for more rigorous testing scenarios where immediate correctness is required for full credit.
This commit is contained in:
dralkh 2025-08-30 15:33:49 +03:00
parent dbd37e8537
commit 4a44db5a2c
6 changed files with 62 additions and 53 deletions

22
main.js
View file

@ -3228,11 +3228,20 @@ var MCQModal = class extends import_obsidian8.Modal {
let totalScore = 0;
this.session.answers.forEach((answer) => {
let questionScore = 0;
if (answer.correct && answer.selectedAnswerIndex !== -1) {
if (answer.attempts === 1)
if (this.plugin.settings.mcqDeductFullMarkOnFirstFailure) {
if (answer.attempts === 1 && answer.correct) {
questionScore = 1;
else if (answer.attempts === 2)
questionScore = 0.5;
} else {
questionScore = 0;
}
} else {
if (answer.correct && answer.selectedAnswerIndex !== -1) {
if (answer.attempts === 1) {
questionScore = 1;
} else if (answer.attempts === 2) {
questionScore = 0.5;
}
}
}
if (questionScore > 0 && answer.timeToAnswer > this.plugin.settings.mcqTimeDeductionSeconds) {
questionScore -= this.plugin.settings.mcqTimeDeductionAmount;
@ -6465,6 +6474,7 @@ var DEFAULT_SETTINGS = {
mcqTimeDeductionAmount: 0.5,
mcqTimeDeductionSeconds: 90,
mcqDifficulty: "advanced" /* Advanced */,
mcqDeductFullMarkOnFirstFailure: false,
mcqBasicSystemPrompt: "You are a tutor who creates clear, straightforward multiple-choice questions to test basic understanding of the given content. Focus on key concepts and important facts. Make questions simple and direct, with one clearly correct answer. Always mark the correct answer with [CORRECT] at the end of the line.",
mcqAdvancedSystemPrompt: "You are an expert tutor who creates challenging but fair multiple-choice questions to test deep understanding of the given content. Generate questions that assess comprehension, application, and analysis, not just memorization. Make incorrect choices plausible to encourage critical thinking. Always mark the correct answer with [CORRECT] at the end of the line.",
// Pomodoro Timer Defaults
@ -7031,6 +7041,10 @@ var SpaceforgeSettingTab = class extends import_obsidian17.PluginSettingTab {
this.plugin.settings.mcqTimeDeductionSeconds = value;
await this.plugin.savePluginData();
}));
new import_obsidian17.Setting(mcqSection).setName("Deduct full mark on first failure").setDesc("If enabled, the score for a question will be 0 if the first attempt is incorrect.").addToggle((toggle) => toggle.setValue(this.plugin.settings.mcqDeductFullMarkOnFirstFailure).onChange(async (value) => {
this.plugin.settings.mcqDeductFullMarkOnFirstFailure = value;
await this.plugin.savePluginData();
}));
const systemPromptsContainer = mcqSection.createEl("details", { cls: "sf-system-prompts-container" });
systemPromptsContainer.createEl("summary", { text: "System Prompts (Advanced)", cls: "sf-settings-subsection" });
systemPromptsContainer.createEl("div", { text: "Basic Difficulty Prompt", cls: "sf-prompt-label" });

View file

@ -179,6 +179,12 @@ export interface SpaceforgeSettings {
*/
mcqDifficulty: MCQDifficulty;
/**
* Deduct full mark for a question if the first attempt is a failure.
* Default: false
*/
mcqDeductFullMarkOnFirstFailure: boolean;
/**
* API Provider for MCQ generation
* Default: 'openrouter'
@ -351,6 +357,7 @@ export const DEFAULT_SETTINGS: SpaceforgeSettings = {
mcqTimeDeductionAmount: 0.5,
mcqTimeDeductionSeconds: 90,
mcqDifficulty: MCQDifficulty.Advanced,
mcqDeductFullMarkOnFirstFailure: true,
mcqBasicSystemPrompt: 'You are a tutor who creates clear, straightforward multiple-choice questions to test basic understanding of the given content. Focus on key concepts and important facts. Make questions simple and direct, with one clearly correct answer. Always mark the correct answer with [CORRECT] at the end of the line.',
mcqAdvancedSystemPrompt: 'You are an expert tutor who creates challenging but fair multiple-choice questions to test deep understanding of the given content. Generate questions that assess comprehension, application, and analysis, not just memorization. Make incorrect choices plausible to encourage critical thinking. Always mark the correct answer with [CORRECT] at the end of the line.',

View file

@ -1494,8 +1494,6 @@ button.disabled:hover {
border-radius: var(--radius-m, 8px);
box-shadow: var(--sf-shadow-md);
}
.is-collapsed .calendar-container-wrapper {
}
.is-collapsed .calendar-container {
padding: var(--sf-space-sm);
}
@ -2369,6 +2367,10 @@ button.disabled:hover {
color: var(--sf-danger);
font-weight: 500;
}
.mcq-result-final-correct {
color: var(--sf-warning);
font-weight: 500;
}
.mcq-result-attempts,
.mcq-result-time {
font-size: 0.9em;
@ -2709,35 +2711,6 @@ button.disabled:hover {
padding: 2px 6px !important;
border-radius: 4px !important;
}
.review-time-short,
.review-time-medium,
.review-time-long,
.sm2-info,
.sm2-ratings-title,
.sm2-ratings-desc,
.sm2-ratings-table,
.sm2-ratings-table th,
.sm2-ratings-table td,
.prompt-setting,
.prompt-textarea,
.sf-settings-section,
.sf-settings-section-header,
.sf-settings-icon,
.sf-settings-section-header h3,
.sf-settings-collapse-indicator,
.sf-settings-section-content,
.sf-settings-subsection,
.sf-setting-explain,
.sf-setting-highlight,
.sf-setting-grid,
.sf-settings-actions,
.sf-info-box,
.sf-prompt-label,
.sf-system-prompts-container,
.sf-danger-zone,
.mcq-shortcut-hint,
.hidden {
}
.mcq-choice-selected {
border-color: var(--interactive-accent) !important;
box-shadow: 0 0 0 2px rgba(var(--interactive-accent-rgb), 0.5), 0 1px 3px rgba(0, 0, 0, 0.1);
@ -2817,8 +2790,6 @@ button.disabled:hover {
font-weight: 500;
color: var(--text-faint);
}
.mcq-breakdown-item .user-answer-text {
}
.mcq-breakdown-item .correctness-indicator {
font-weight: bold;
}
@ -3005,8 +2976,6 @@ button.disabled:hover {
.pomodoro-quick-save-btn {
display: none;
}
.pomodoro-quick-save-btn:hover {
}
.pomodoro-quick-calculate-btn {
background-color: var(--background-modifier-border);
color: var(--text-normal);

View file

@ -486,6 +486,10 @@
color: var(--sf-danger);
font-weight: 500;
}
.mcq-result-final-correct {
color: var(--sf-warning);
font-weight: 500;
}
.mcq-result-attempts,
.mcq-result-time {
@ -889,14 +893,6 @@
}
/* Preserve legacy class function while improving appearance */
.review-time-short, .review-time-medium, .review-time-long,
.sm2-info, .sm2-ratings-title, .sm2-ratings-desc, .sm2-ratings-table, .sm2-ratings-table th, .sm2-ratings-table td,
.prompt-setting, .prompt-textarea, .sf-settings-section, .sf-settings-section-header, .sf-settings-icon,
.sf-settings-section-header h3, .sf-settings-collapse-indicator, .sf-settings-section-content, .sf-settings-subsection,
.sf-setting-explain, .sf-setting-highlight, .sf-setting-grid, .sf-settings-actions, .sf-info-box, .sf-prompt-label,
.sf-system-prompts-container, .sf-danger-zone, .mcq-shortcut-hint, .hidden {
/* Preserve the classes for compatibility */
}
/* Style for the selected answer using keyboard navigation */
.mcq-choice-selected {
@ -999,9 +995,6 @@
}
/* Specific styling for the user's answer text and correctness indicator */
.mcq-breakdown-item .user-answer-text { /* Class to be added in JS if needed for more specific targeting */
/* (color is set dynamically in JS) */
}
.mcq-breakdown-item .correctness-indicator { /* Class to be added in JS */
font-weight: bold;

View file

@ -456,10 +456,26 @@ export class MCQModal extends Modal {
let totalScore = 0;
this.session.answers.forEach(answer => {
let questionScore = 0;
if (answer.correct && answer.selectedAnswerIndex !== -1) {
if (answer.attempts === 1) questionScore = 1.0;
else if (answer.attempts === 2) questionScore = 0.5;
if (this.plugin.settings.mcqDeductFullMarkOnFirstFailure) {
// With this setting, you only get points if you're correct on the first try.
if (answer.attempts === 1 && answer.correct) {
questionScore = 1.0;
} else {
questionScore = 0; // 0 for multiple attempts or for a single incorrect attempt.
}
} else {
// Original scoring logic
if (answer.correct && answer.selectedAnswerIndex !== -1) {
if (answer.attempts === 1) {
questionScore = 1.0;
} else if (answer.attempts === 2) {
questionScore = 0.5;
}
}
}
// Apply time deduction only if points were scored.
if (questionScore > 0 && answer.timeToAnswer > this.plugin.settings.mcqTimeDeductionSeconds) {
questionScore -= this.plugin.settings.mcqTimeDeductionAmount;
questionScore = Math.max(0, questionScore);

View file

@ -937,6 +937,16 @@ export class SpaceforgeSettingTab extends PluginSettingTab {
this.plugin.settings.mcqTimeDeductionSeconds = value;
await this.plugin.savePluginData();
}));
new Setting(mcqSection)
.setName('Deduct full mark on first failure')
.setDesc('If enabled, the score for a question will be 0 if the first attempt is incorrect.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.mcqDeductFullMarkOnFirstFailure)
.onChange(async (value: boolean) => {
this.plugin.settings.mcqDeductFullMarkOnFirstFailure = value;
await this.plugin.savePluginData();
}));
// Add collapsible section for system prompts
const systemPromptsContainer = mcqSection.createEl('details', { cls: 'sf-system-prompts-container' });