如何使用 SCORM 和 React 计算总分?
how to calculate total score with SCORM and React?
关于SCORM实现的例子很少,所以我有点迷茫。我需要根据 3 个问题获得学生分数。我真的不明白我在做什么。我知道我可能需要一个像 calculateScore() 这样的函数。不过,SCORM 把我搞糊涂了。我使用 SCORMCLOUD 进行测试,但每次我想测试一些东西时我都会重新上传构建..
APP.js
function App() {
Scorm.init();
const [learnerName, setLearnerName] = useState(`${Scorm.getLearnerName()}`);
const [assessment, setAssessment] = useState([]);
const finish = () => {
Scorm.finish();
};
const updateAssesment = (correct, response) => {
setAssessment(assessment.concat([correct]));
Scorm.submitMCQ(correct, response);
};
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Learner name={learnerName} />
</header>
<main>
<Mcq result={updateAssesment.bind()} question="What is 10 * 10?" correctAnswer={0} answers={["100", "20"]} />
<Mcq
result={updateAssesment.bind()}
question="What is the capital of Spain?"
correctAnswer={2}
answers={["Barcelona", "Lisbon", "Madrid"]}
/>
<Mcq
result={updateAssesment.bind()}
question="Which US President's office commissioned the creation of SCORM?"
correctAnswer={3}
answers={["Donald Trump", "Barack Obama", "Ronald Reagan", "Bill Clinton"]}
/>
<CompleteButton completeActivity={finish.bind()} />
</main>
</div>
);
}
export default App;
SCORM.js
import { SCORM } from "pipwerks-scorm-api-wrapper";
let Scorm = {
init() {
SCORM.init();
},
getLearnerName() {
return SCORM.get("cmi.core.student_name");
},
submitMCQ(correct, response) {
let nextIndex = SCORM.get("cmi.interactions._count", true);
SCORM.set("cmi.interactions." + nextIndex + ".id", "round_" + nextIndex);
SCORM.set("cmi.interactions." + nextIndex + ".type", "choice");
SCORM.set("cmi.interactions." + nextIndex + ".student_response", response);
SCORM.set("cmi.interactions." + nextIndex + ".result", correct);
},
calculateScore() {
//something here??
SCORM.set("cmi.core.score.raw", "0");
SCORM.set("cmi.core.score.max", "100");
SCORM.set("cmi.core.score.min", "0");
},
finish() {
alert("you have finished!");
SCORM.set("cmi.core.lesson_status", "completed");
SCORM.save();
SCORM.quit();
},
};
export default Scorm;
MCQ 组件
export default function Mcq(props) {
const [selectedOption, setSelectedOption] = useState(0);
const [answered, setAnswered] = useState(false);
const handleOptionChange = (changeEvent) => {
setSelectedOption(Number(changeEvent.target.value));
};
const renderAnswers = () => {
return props.answers.map(function (answer, index) {
return (
<div className="answer" key={index}>
<input type="radio" value={index} checked={selectedOption === index} onChange={handleOptionChange} />
<label>{answer}</label>
</div>
);
});
};
const handleFormSubmit = (formSubmitEvent) => {
formSubmitEvent.preventDefault();
setAnswered(true);
props.result(selectedOption === props.correctAnswer, props.answers[selectedOption]);
};
const currentState = () => {
if (!answered) {
return (
<form onSubmit={handleFormSubmit.bind(this)}>
{renderAnswers()}
<button className="btn btn-default" type="submit">
Submit
</button>
</form>
);
} else {
return <div>{checkCorrectAnswer()}</div>;
}
};
const checkCorrectAnswer = () => {
if (selectedOption === props.correctAnswer) {
return `yes, ${props.answers[props.correctAnswer]} is the correct answer.`;
} else {
return `You answered ${props.answers[selectedOption]}. Sorry, but the correct answer is ${
props.answers[props.correctAnswer]
}.`;
}
};
return (
<div className="Mcq">
<p>{props.question}</p>
{currentState()}
</div>
);
}
你的问题有点令人费解。 SCORM 是 API 将两件事联系在一起:
- 学习内容,通常采用一个或多个所谓的 SCO 的形式(即 HTML 包含与 SCORM JS 对话的资产的文件 API)
- LMS 服务于学习内容并提供 SCORM JS API 实例
看起来你正在尝试创建学习内容,尽管你提供了一种更改学习者姓名的方法,这是 JS 提供的只读数据 API。
如果这就是您想要做的,那么 SCORM 不会告诉您分数,而是您自己。根据您的代码,我猜您希望每个问题在回答正确的情况下都值得多分(或至少只有一分),并且分数应基于该问题和问题总数。
由于您已经将所有答案存储在 cmi.interactions
中,您可以先尝试获取互动次数(即答案)。这会告诉您答案总数,因此会告诉您最大分数,或 cmi.core.score.max
.
因为如果你做一个简单的计数而不是更复杂的事情,比如选择不正确的选择的负分,最低可能的分数是零分,那么 cmi.core.score.min
将是 0
。
cmi.core.score.raw
就是总分,也就是正确答案的个数。你可以通过遍历你之前设置的 cmi.interactions.n.result
的值来得到这个(n
是零和 cmi.interactions._count
之间的索引,并且只计算 result
是 "correct"
).
但是请注意,submitMCQ
应将 cmi.interactions.n.result
设置为 "correct"
或 "incorrect"
而不是 true
或 false
。否则,符合标准的 LMS 将拒绝该值作为无效值(这不会引发异常,而是设置一个错误标志,您必须明确检查,因为 SCORM 很奇怪)。
我设计了 SCOBot Content API 来帮助解决其中的一些问题,如果您想试一试的话。至少如果您厌倦了尝试查找所有个人电话。
它添加了额外的包装来管理总交互/评分计算,因此您无需编写所有额外的管理。
还有示例分组,这样您只需定义一次,它就会处理 -
https://github.com/cybercussion/SCOBot/wiki/SCORM-SCOBot-Documentation#set-interaction
有许多配置设置可以将项目的 'bot' 部分变为 on/off 等...
QUnit 测试也写在测试目录中,其中显示了几个 interactions/objectives 和其他调用。
https://github.com/cybercussion/SCOBot/blob/e67239c36fdc104be9d29b0810815f9b3175c831/QUnit-Tests/js/test/scobot-prod.js#L252
我还将它设计为独立于 1.2 / 2004 工作,因此您使用的是一组命名空间。而且不必记住所有版本差异。
本项目开源,免费use/test出。最新版本将其从 jQuery 中分离出来,因此它有自己的事件系统和实用程序。如果你使用像 Angular 这样的东西,你可能必须使用 NgZone,React 我想你可能没问题。如果您 运行 遇到问题请告诉我。
关于SCORM实现的例子很少,所以我有点迷茫。我需要根据 3 个问题获得学生分数。我真的不明白我在做什么。我知道我可能需要一个像 calculateScore() 这样的函数。不过,SCORM 把我搞糊涂了。我使用 SCORMCLOUD 进行测试,但每次我想测试一些东西时我都会重新上传构建..
APP.js
function App() {
Scorm.init();
const [learnerName, setLearnerName] = useState(`${Scorm.getLearnerName()}`);
const [assessment, setAssessment] = useState([]);
const finish = () => {
Scorm.finish();
};
const updateAssesment = (correct, response) => {
setAssessment(assessment.concat([correct]));
Scorm.submitMCQ(correct, response);
};
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Learner name={learnerName} />
</header>
<main>
<Mcq result={updateAssesment.bind()} question="What is 10 * 10?" correctAnswer={0} answers={["100", "20"]} />
<Mcq
result={updateAssesment.bind()}
question="What is the capital of Spain?"
correctAnswer={2}
answers={["Barcelona", "Lisbon", "Madrid"]}
/>
<Mcq
result={updateAssesment.bind()}
question="Which US President's office commissioned the creation of SCORM?"
correctAnswer={3}
answers={["Donald Trump", "Barack Obama", "Ronald Reagan", "Bill Clinton"]}
/>
<CompleteButton completeActivity={finish.bind()} />
</main>
</div>
);
}
export default App;
SCORM.js
import { SCORM } from "pipwerks-scorm-api-wrapper";
let Scorm = {
init() {
SCORM.init();
},
getLearnerName() {
return SCORM.get("cmi.core.student_name");
},
submitMCQ(correct, response) {
let nextIndex = SCORM.get("cmi.interactions._count", true);
SCORM.set("cmi.interactions." + nextIndex + ".id", "round_" + nextIndex);
SCORM.set("cmi.interactions." + nextIndex + ".type", "choice");
SCORM.set("cmi.interactions." + nextIndex + ".student_response", response);
SCORM.set("cmi.interactions." + nextIndex + ".result", correct);
},
calculateScore() {
//something here??
SCORM.set("cmi.core.score.raw", "0");
SCORM.set("cmi.core.score.max", "100");
SCORM.set("cmi.core.score.min", "0");
},
finish() {
alert("you have finished!");
SCORM.set("cmi.core.lesson_status", "completed");
SCORM.save();
SCORM.quit();
},
};
export default Scorm;
MCQ 组件
export default function Mcq(props) {
const [selectedOption, setSelectedOption] = useState(0);
const [answered, setAnswered] = useState(false);
const handleOptionChange = (changeEvent) => {
setSelectedOption(Number(changeEvent.target.value));
};
const renderAnswers = () => {
return props.answers.map(function (answer, index) {
return (
<div className="answer" key={index}>
<input type="radio" value={index} checked={selectedOption === index} onChange={handleOptionChange} />
<label>{answer}</label>
</div>
);
});
};
const handleFormSubmit = (formSubmitEvent) => {
formSubmitEvent.preventDefault();
setAnswered(true);
props.result(selectedOption === props.correctAnswer, props.answers[selectedOption]);
};
const currentState = () => {
if (!answered) {
return (
<form onSubmit={handleFormSubmit.bind(this)}>
{renderAnswers()}
<button className="btn btn-default" type="submit">
Submit
</button>
</form>
);
} else {
return <div>{checkCorrectAnswer()}</div>;
}
};
const checkCorrectAnswer = () => {
if (selectedOption === props.correctAnswer) {
return `yes, ${props.answers[props.correctAnswer]} is the correct answer.`;
} else {
return `You answered ${props.answers[selectedOption]}. Sorry, but the correct answer is ${
props.answers[props.correctAnswer]
}.`;
}
};
return (
<div className="Mcq">
<p>{props.question}</p>
{currentState()}
</div>
);
}
你的问题有点令人费解。 SCORM 是 API 将两件事联系在一起:
- 学习内容,通常采用一个或多个所谓的 SCO 的形式(即 HTML 包含与 SCORM JS 对话的资产的文件 API)
- LMS 服务于学习内容并提供 SCORM JS API 实例
看起来你正在尝试创建学习内容,尽管你提供了一种更改学习者姓名的方法,这是 JS 提供的只读数据 API。
如果这就是您想要做的,那么 SCORM 不会告诉您分数,而是您自己。根据您的代码,我猜您希望每个问题在回答正确的情况下都值得多分(或至少只有一分),并且分数应基于该问题和问题总数。
由于您已经将所有答案存储在 cmi.interactions
中,您可以先尝试获取互动次数(即答案)。这会告诉您答案总数,因此会告诉您最大分数,或 cmi.core.score.max
.
因为如果你做一个简单的计数而不是更复杂的事情,比如选择不正确的选择的负分,最低可能的分数是零分,那么 cmi.core.score.min
将是 0
。
cmi.core.score.raw
就是总分,也就是正确答案的个数。你可以通过遍历你之前设置的 cmi.interactions.n.result
的值来得到这个(n
是零和 cmi.interactions._count
之间的索引,并且只计算 result
是 "correct"
).
但是请注意,submitMCQ
应将 cmi.interactions.n.result
设置为 "correct"
或 "incorrect"
而不是 true
或 false
。否则,符合标准的 LMS 将拒绝该值作为无效值(这不会引发异常,而是设置一个错误标志,您必须明确检查,因为 SCORM 很奇怪)。
我设计了 SCOBot Content API 来帮助解决其中的一些问题,如果您想试一试的话。至少如果您厌倦了尝试查找所有个人电话。
它添加了额外的包装来管理总交互/评分计算,因此您无需编写所有额外的管理。
还有示例分组,这样您只需定义一次,它就会处理 - https://github.com/cybercussion/SCOBot/wiki/SCORM-SCOBot-Documentation#set-interaction
有许多配置设置可以将项目的 'bot' 部分变为 on/off 等...
QUnit 测试也写在测试目录中,其中显示了几个 interactions/objectives 和其他调用。 https://github.com/cybercussion/SCOBot/blob/e67239c36fdc104be9d29b0810815f9b3175c831/QUnit-Tests/js/test/scobot-prod.js#L252
我还将它设计为独立于 1.2 / 2004 工作,因此您使用的是一组命名空间。而且不必记住所有版本差异。
本项目开源,免费use/test出。最新版本将其从 jQuery 中分离出来,因此它有自己的事件系统和实用程序。如果你使用像 Angular 这样的东西,你可能必须使用 NgZone,React 我想你可能没问题。如果您 运行 遇到问题请告诉我。