reactjsscormscorm1.2scorm-cloud-api

how to calculate total score with SCORM and React?


There are very few example on SCORM implementation, so I am a little lost. I need to get the student score based on 3 questions. I don't really understand what i am doing. I know i probably need a function like calculateScore(). SCORM is confusing the crap out of me though. I use SCORMCLOUD to test but i have reupload the build everytime i wanna test something..

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 component

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>
  );
}

Solution

  • Your question is a bit confusing. SCORM is an API tying two things together:

    It looks like you are trying to create learning content, although you provide a way to change the learner name, which is read-only data provided by the JS API.

    If that's what you're trying to do, it's not up to SCORM to tell you the score but to you. Based on your code I'm guessing you want each question to be worth a number of points (or at least just one) if answered correctly and the score should be based on that and the total number of questions.

    Since you're storing all answers in cmi.interactions already, you could try getting the number of interactions (i.e. answers) first. This tells you the total number of answers and thus the maximum number of points, or cmi.core.score.max.

    Since the lowest possible score is zero points if you do a simple tally instead of something more complicated like negative points for incorrect choices being selected, the cmi.core.score.min would then be 0.

    The cmi.core.score.raw would be the total number of points, so the number of answers that are correct. You could get this by iterating over the values of cmi.interactions.n.result you set previously (with n being the index between zero and cmi.interactions._count and only counting those where the result is "correct").

    However note that submitMCQ should set cmi.interactions.n.result to "correct" or "incorrect" not true or false. A conforming LMS will otherwise reject the value as invalid (which does not raise an exception but instead sets an error flag you have to check explicitly because SCORM is weird like that).