Package org.olat.ims.qti.container

Source Code of org.olat.ims.qti.container.AssessmentContext

/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/

package org.olat.ims.qti.container;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.Element;
import org.olat.ims.qti.container.qtielements.AssessFeedback;
import org.olat.ims.qti.container.qtielements.Objectives;
import org.olat.ims.qti.process.AssessmentInstance;
import org.olat.ims.qti.process.QTIHelper;
import org.olat.ims.qti.process.elements.ScoreBooleanEvaluable;

/**
* contains the sections of the assignment. assumption: each toplevel-section of
* an assignment means one screen <!ELEMENT assessment (qticomment? , duration? ,
* qtimetadata* , objectives* , assessmentcontrol* , rubric* ,
* presentation_material? , outcomes_processing* , assessproc_extension? ,
* assessfeedback* , selection_ordering? , reference? , (sectionref |
* section)+)> <!ATTLIST assessment %I_Ident; %I_Title; xml:lang CDATA #IMPLIED >
*
* @author Felix Jost
*/
public class AssessmentContext implements Serializable {
  // readonly ref!: the ref to the el_assessment; transient since it we don't
  // want to serialize it (too long) and can reattach it later
  //private transient Element el_assessment;
  private String ident;
  private String title;
  private AssessmentInstance assessInstance;

  private Element el_assessment;
  private Objectives objectives;
  private Switches switches = null;
  private Output output;

  // the sectioncontexts of this assessment
  private List sectionContexts;

  // the current section beeing chosen by the user or forced by the system
  private int currentSectionContextPos;
  private long timeOfStart;
  // server time at the time of the start of the assessment
  private long timeOfStop;
  // server time at the time of the start of the assessment
  private long durationLimit; //
  private float cutvalue = -1.0f;
  private String scoremodel;
  private boolean feedbacktesting;
  private boolean feedbackavailable;

  /**
   * default constructor needed for persistence
   */
  public AssessmentContext() {
  //
  }

  /**
   *
   */
  public void init() {
    currentSectionContextPos = -1;
    feedbacktesting = false;
    feedbackavailable = false;
    timeOfStart = -1; // not started yet
    timeOfStop = -1; // not stopped yet
  }

  /**
   * Method setUp.
   *
   * @param assessInstance
   */
  public void setUp(AssessmentInstance assessInstance) {
    this.assessInstance = assessInstance;
    init();

    Document el_questestinterop = assessInstance.getResolver().getQTIDocument();
    el_assessment = (Element) el_questestinterop.selectSingleNode("questestinterop/assessment");

    ident = el_assessment.attributeValue("ident");
    title = el_assessment.attributeValue("title");
    Element dur = (Element) el_assessment.selectSingleNode("duration");

    if (dur == null) {
      durationLimit = -1; // no limit
    } else {
      String sdur = dur.getText();
      durationLimit = QTIHelper.parseISODuration(sdur);
      if (durationLimit == 0) durationLimit = -1; // Assesst Designer fix
    }

    // get objectives
    Element el_objectives = (Element)el_assessment.selectSingleNode("objectives");
    if (el_objectives != null) objectives = new Objectives(el_objectives);
   
    // set feedback, hint, and solutions switches
    //<!ENTITY % I_FeedbackSwitch " feedbackswitch (Yes | No ) 'Yes'">
    //<!ENTITY % I_HintSwitch " hintswitch (Yes | No ) 'Yes'">
    //<!ENTITY % I_SolutionSwitch " solutionswitch (Yes | No ) 'Yes'">

    //<!ELEMENT assessment (qticomment? , duration? , qtimetadata* ,
    // objectives* , assessmentcontrol* , rubric* , presentation_material? ,
    // outcomes_processing* , assessproc_extension? , assessfeedback* ,
    // selection_ordering? , reference? , (sectionref | section)+)>
    //<!ELEMENT assessmentcontrol (qticomment?)>
    Element el_control = (Element) el_assessment.selectSingleNode("assessmentcontrol");
    if (el_control != null) {
      String feedbackswitch = el_control.attributeValue("feedbackswitch");
      String hintswitch = el_control.attributeValue("hintswitch");
      String solutionswitch = el_control.attributeValue("solutionswitch");
      boolean feedback = (feedbackswitch == null) ? true : feedbackswitch.equals("Yes");
      boolean hints = (hintswitch == null) ? true : hintswitch.equals("Yes");
      boolean solutions = (solutionswitch == null) ? true : solutionswitch.equals("Yes");
      switches = new Switches(feedback, hints, solutions);
    }

    // scoring model and outcomes processing
    Element el_outpro = (Element) el_assessment.selectSingleNode("outcomes_processing");
    if (el_outpro != null) {
      // get the scoring model: we need it later for calculating the score
      //<!ENTITY % I_ScoreModel " scoremodel CDATA #IMPLIED">
      scoremodel = el_outpro.attributeValue("scoremodel");
      // may be null -> then assume SumOfScores

      // set the cutvalue if given (only variable score)
      cutvalue = QTIHelper.getFloatAttribute(el_outpro, "outcomes/decvar[@varname='SCORE']", "cutvalue");
      List el_oft = el_outpro.selectNodes("outcomes_feedback_test");
      if (el_oft.size() != 0) {
        feedbacktesting = true;
      }
    }

    initSections(el_assessment, switches);
    init();
  }

  private void initSections(Element assessment, Switches sw) {
    sectionContexts = new ArrayList(2);

    //<!ELEMENT sectionref (#PCDATA)>
    //<!ATTLIST sectionref %I_LinkRefId; >
    List sections = assessment.selectNodes("section|sectionref");

    for (Iterator iter = sections.iterator(); iter.hasNext();) {
      Element el_section = (Element) iter.next();

      // resolve sectionref into the correct sections
      if (el_section.getName().equals("sectionref")) {
        String linkRefId = el_section.attributeValue("linkrefid");
        el_section = (Element) el_section.selectSingleNode("//section[@ident='" + linkRefId + "']");
        if (el_section == null) { throw new RuntimeException("sectionref with ref '" + linkRefId + "' could not be resolved"); }
      }

      SectionContext sc = new SectionContext();
      sc.setUp(assessInstance, el_section, sw);
      sectionContexts.add(sc);
    }
  }

  /**
   * start assessment
   */
  public void start() {
    // if not started yet, start
    if (timeOfStart == -1) {
      timeOfStart = System.currentTimeMillis();
    }
  }

  /**
   * stop assessment
   */
  public void stop() {
    if (timeOfStart != -1 && timeOfStop == -1) {
      timeOfStop = System.currentTimeMillis();
    }
    if (getCurrentSectionContext() != null) getCurrentSectionContext().sectionWasSubmitted();
  }

  /**
   *
   */
  public void eval() {
    if (assessInstance.isSurvey()) return;
    int sccnt = getSectionContextCount();
    for (int i = 0; i < sccnt; i++) {
      SectionContext sc = getSectionContext(i);
      sc.eval();
    }
    if (feedbacktesting) calcFeedBack();
  }

  /**
   * @see java.lang.Object#toString()
   */
  public String toString() {
    return "<br /><br />assessment:" + sectionContexts.toString() + "=" + super.toString();
  }

  /**
   * Method getIdent.
   *
   * @return String
   */
  public String getIdent() {
    return ident;
  }

  /**
   * @return
   */
  public String getTitle() {
    return title;
  }

  /**
   * @return
   */
  public SectionContext getCurrentSectionContext() {
    if (currentSectionContextPos == -1) return null;
    SectionContext sc = (SectionContext) sectionContexts.get(currentSectionContextPos);
    return sc;
  }

  /**
   * Sets the currentSectionPos.
   *
   * @param currentSectionPos The currentSectionPos to set
   */
  public void setCurrentSectionPos(int currentSectionPos) {
    if (currentSectionPos >= sectionContexts.size()) { throw new RuntimeException("error"); }
    this.currentSectionContextPos = currentSectionPos;
  }

  /**
   * Method getSectionContextCount.
   *
   * @return int
   */
  public int getSectionContextCount() {
    return sectionContexts.size();
  }

  /**
   * Return the total items in all sections of the assessment.
   * @return Total number of items
   */
  public int getItemContextCount() {
    int count = 0;
    int sccnt = getSectionContextCount();

    for (int i = 0; i < sccnt; i++) {
      SectionContext sc = getSectionContext(i);
      count += sc.getItemContextCount();
    }
    return count;
  }

  /**
   * Get the position of the current item within the assessment.
   * @return position of the current item within the assessment.
   */
  public int getItemPosWithinAssessment() {
    if (currentSectionContextPos == -1) return 1; // first question
    int currentPos = 1;
    for (int i=0; i < getCurrentSectionContextPos(); i++) {
      // count all items in previous section
      currentPos += getSectionContext(i).getItemContextCount();
    }
    SectionContext curSectionContext = getCurrentSectionContext();
    if (curSectionContext.getCurrentItemContextPos() != -1)
      // this is a section page, just add 1 item to the current pos
      currentPos += curSectionContext.getCurrentItemContextPos();
    return currentPos;
  }
 
  /**
   * Method setCurrentSectionContextPos.
   *
   * @param i
   */
  public void setCurrentSectionContextPos(int i) {
    currentSectionContextPos = i;
  }

  /**
   * Returns the currentSectionContextPos.
   *
   * @return int
   */
  public int getCurrentSectionContextPos() {
    return currentSectionContextPos;
  }

  /**
   * checks whether the user may still submit answers
   *
   * @return
   */
  public boolean isOpen() {
    // not started yet or no timelimit or within timelimit
    return (timeOfStart == -1) || (durationLimit == -1) || (System.currentTimeMillis() < (timeOfStart + durationLimit));
  }

  /**
   * @return
   */
  public boolean isStarted() {
    return (timeOfStart != -1);
  }

  /**
   * @param pos
   * @return
   */
  public SectionContext getSectionContext(int pos) {
    return (SectionContext) sectionContexts.get(pos);
  }

  /**
   * @return long
   */
  public long getDurationLimit() {
    return durationLimit;
  }

  /**
   * Return the time to completion for this assessment
   *
   * @return long Millis to completion
   */
  public long getDuration() {
    if (timeOfStart == -1 | timeOfStop == -1) return 0;
    return timeOfStop - timeOfStart;
  }

  /**
   * Get the maximum score for this assessment. (Sum of maxscore of all items)
   *
   * @return
   */
  public float getMaxScore() {
    float count = 0.0f;
    for (Iterator iter = sectionContexts.iterator(); iter.hasNext();) {
      SectionContext sc = (SectionContext) iter.next();
      float maxScore = sc.getMaxScore();
      if (maxScore == -1) return -1;
      else count += maxScore;
    }
    return count;
  }

  /**
   * @return
   */
  public float getScore() {
    if (scoremodel == null || scoremodel.equalsIgnoreCase("SumOfScores")) { // sumofScores

      float count = 0;
      for (Iterator iter = sectionContexts.iterator(); iter.hasNext();) {
        SectionContext sc = (SectionContext) iter.next();
        count += sc.getScore();
      }
      return count;
    } else if (scoremodel.equalsIgnoreCase("NumberCorrect")) {
      float tmpscore = 0.0f;
      // calculate correct number of sections: an section is correct if its
      // correct items reach the section's cutvalue
      for (Iterator iter = sectionContexts.iterator(); iter.hasNext();) {
        SectionContext sc = (SectionContext) iter.next();
        float sscore = sc.getScore();
        if (sscore >= cutvalue) tmpscore++; // count items correct
      }
      return tmpscore;
    } else {
      throw new RuntimeException("scoring algorithm " + scoremodel + " not supported");
    }

  }

  /**
   * @return
   */
  public boolean isPassed() {
    float score = getScore();
    return (score >= cutvalue);
  }

  /**
   * @return
   */
  public int getItemsPresentedCount() {
    int count = 0;
    for (Iterator iter = sectionContexts.iterator(); iter.hasNext();) {
      SectionContext sc = (SectionContext) iter.next();
      count += sc.getItemsPresentedCount();
    }
    return count;
  }

  /**
   * @return
   */
  public int getItemsAttemptedCount() {
    int count = 0;
    for (Iterator iter = sectionContexts.iterator(); iter.hasNext();) {
      SectionContext sc = (SectionContext) iter.next();
      count += sc.getItemsAttemptedCount();
    }
    return count;
  }

  /**
   * Method calcFeedBack.
   */
  private void calcFeedBack() {
    if (feedbacktesting) {
      List el_ofts = el_assessment.selectNodes("outcomes_processing/outcomes_feedback_test");
      feedbackavailable = false;
      for (Iterator it_oft = el_ofts.iterator(); it_oft.hasNext();) {
        Element el_oft = (Element) it_oft.next();
        //<!ELEMENT outcomes_feedback_test (test_variable , displayfeedback+)>
        Element el_testvar = (Element) el_oft.selectSingleNode("test_variable");
        // must exist: dtd
        //<!ELEMENT test_variable (variable_test | and_test | or_test |
        // not_test)>
        Element el_varandornot = (Element) el_testvar.selectSingleNode("variable_test|and_test|or_test|not_test");
        String elname = el_varandornot.getName();
        ScoreBooleanEvaluable sbe = QTIHelper.getSectionBooleanEvaluableInstance(elname);
        float totalscore = getScore();
        boolean fulfilled = sbe.eval(el_varandornot, totalscore);
        if (fulfilled) {
          // get feedback
          Element el_displayfeedback = (Element) el_oft.selectSingleNode("displayfeedback");
          String linkRefId = el_displayfeedback.attributeValue("linkrefid");
          // must exist (dtd)
          // ignore feedbacktype, since we section or assess feedback only
          // accepts material, no hints or solutions
          Element el_resolved = (Element) el_assessment.selectSingleNode(".//assessfeedback[@ident='" + linkRefId + "']");
          getOutput().setEl_response(new AssessFeedback(el_resolved));
          // give the whole assessmentfeedback to render
          feedbackavailable = true;
        }
      }
    }
  }

  /**
   * @return Output
   */
  public Output getOutput() {
    if (output == null) {
      output = new Output();
    }
    return output;
  }

  /**
   * @return
   */
  public Switches getSwitches() {
    return switches;
  }

  /**
   * @param switches
   */
  public void setSwitches(Switches switches) {
    this.switches = switches;
  }

  /**
   * @return
   */
  public boolean isFeedbackavailable() {
    return feedbackavailable;
  }

  /**
   * @param b
   */
  public void setFeedbackavailable(boolean b) {
    feedbackavailable = b;
  }

  /**
   * @return float
   */
  public float getCutvalue() {
    return cutvalue;
  }

  /**
   * @return
   */
  public long getTimeOfStart() {
    return timeOfStart;
  }

  /**
   * @return
   */
  public long getTimeOfStop() {
    return timeOfStop;
  }

  public Objectives getObjectives() {
    return objectives;
  }
}
TOP

Related Classes of org.olat.ims.qti.container.AssessmentContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.