Package org.olat.ims.qti.container

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

/**
* 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.Collections;
import java.util.Iterator;
import java.util.List;

import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.olat.ims.qti.QTIConstants;
import org.olat.ims.qti.container.qtielements.Item;
import org.olat.ims.qti.process.AssessmentInstance;
import org.olat.ims.qti.process.QTIHelper;
import org.olat.ims.qti.process.elements.QTI_item;

/**
* @author Potable Shop
*         restrictions on items so far: - only one resprocessing element is
*         evaluated. duration: from ims qti 1.2.1 best practice: duration The
*         duration element is used within the item, section and assessment
*         elements to define the permitted duration for the enclosed activity.
*         The duration is defined as the period between the activity's
*         activation and completion. The ISO 8601 format is used:
*         PnYnMnDTnHnMnS. in mchc_smimr_104.xml example: 0000:00:00T:01:00:00d
*         in ...101.xml: pTH02 iso website:
*         http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html
*         and http://www.w3.org/TR/NOTE-datetime from some page: specified in
*         numbers of years [Y], months [M] or weeks [W], days [D], hours [H]
*         and so on, introduced by a ?P?, for example: P6W2D -> not a date, but
*         only a duration makes sense: -> I shall take the ISO 8601 Time of the
*         day hh:mm:ss format for now
*/
public class ItemContext implements Serializable {

  private AssessmentInstance assessInstance;

  // TODO: no, complicated, since in several objectbanks.... readonly ref!: the
  // ref to the el_item; transient since it we don't want to serialize it (too
  // long) and can reattach it later
  private Element el_shuffled_item;

  // Internal representation of an item.
  private Item qtiItem;
 
  // the outcome of a item after it was evaluated.
  // it contains variables with values
  private Variables variables;

  // contains the output after an evaluation like hint, solution, feedback
  private Output output;

  // the last answer of the user concerning this question
  private ItemInput itemInput;

  // number of times this question has been answered
  private int timesAnswered;
  private long timeOfStart;
  // server time (in miliseconds) at the time of the start of the item
  private long latestAnswerTime;
  // server time (in miliseconds) at the time of the latest answering of the
  // item

  // the ident of the item - needed to reattach the correct element of the
  // qti-tree
  // after a deserialize in case of a crash.
  private String ident;

  // -- the following are also in the qti tree, but are here for convenience ---

  // max number of attempts the user may try this question
  private int maxAttempts; // -1 for infinite
  private long durationLimit; //  
  private boolean evalNeeded;

  private boolean feedback = true; // flags
  private boolean hints = true;
  private boolean solutions = true;

  private int hintLevel;

  //

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

  /**
   *
   */
  public void init() {
    ident = el_shuffled_item.attributeValue("ident");
    resetVariables();
    itemInput = null;
    timesAnswered = 0;
    evalNeeded = false;
    output = new Output();
    timeOfStart = -1; // not started yet
    latestAnswerTime = -1; // not yet answered
    hintLevel = 0; // no hint with type "Incremental" has been given so far
    Element dur = (Element) el_shuffled_item.selectSingleNode("duration");
    if (dur == null) {
      durationLimit = -1; // no limit
    } else {
      String sdur = dur.getText();
      durationLimit = QTIHelper.parseISODuration(sdur);
    }
    String strmaxattempts = el_shuffled_item.attributeValue("maxattempts");
    if (strmaxattempts == null) {
      maxAttempts = -1; // no limit
    } else {
      maxAttempts = Integer.parseInt(strmaxattempts);
    }
  }

  /**
   * Method setUp.
   *
   * @param assessInstance
   * @param el_orig_item
   * @param sw
   */
  public void setUp(AssessmentInstance assessInstance, Element el_orig_item, Switches sw) {
    this.assessInstance = assessInstance;
    this.el_shuffled_item = shuffle(el_orig_item);
    this.qtiItem = new Item(el_shuffled_item);
   
    if (sw == null) { // no section switches dominate, take item switches
      // retrieve item switches
      Element el_control = (Element) el_orig_item.selectSingleNode("itemcontrol");
      if (el_control != null) {
        String feedbackswitch = el_control.attributeValue("feedbackswitch");
        String hintswitch = el_control.attributeValue("hintswitch");
        String solutionswitch = el_control.attributeValue("solutionswitch");
        boolean newFeedback = (feedbackswitch == null) ? true : feedbackswitch.equals("Yes");
        boolean newHints = (hintswitch == null) ? true : hintswitch.equals("Yes");
        boolean newSolutions = (solutionswitch == null) ? true : solutionswitch.equals("Yes");
        sw = new Switches(newFeedback, newHints, newSolutions);
      }
    }

    this.feedback = (sw != null ? sw.isFeedback() : true);
    this.solutions = (sw != null ? sw.isSolutions() : true);
    this.hints = (sw != null ? sw.isHints() : true);
    init();
  }

  /**
   * Method shuffle. shuffle clones the current item (since the whole qti tree
   * is readonly) and shuffles it
   *
   * @param item
   * @return Element
   */
  private Element shuffle(Element item) {
    // get the render_choice
    XPath choice = DocumentHelper.createXPath(".//render_choice[@shuffle=\"Yes\"]");
    Element tel_rendchoice = (Element) choice.selectSingleNode(item);
    //if shuffle is disable, just return the item
    if (tel_rendchoice == null) return item;
    // else: we have to shuffle
    // assume: all response_label have same parent: either render_choice or a
    // flow_label
    Element shuffleItem = item.createCopy();
    // clone the whole item
    Element el_rendchoice = (Element) choice.selectSingleNode(shuffleItem);
    //  <!ELEMENT render_choice ((material | material_ref | response_label |
    // flow_label)* ,response_na?)>
    // <!ATTLIST response_label rshuffle (Yes | No ) 'Yes' .....
    List el_labels = el_rendchoice.selectNodes(".//response_label[@rshuffle=\"Yes\"]");
    int shusize = el_labels.size();

    // set up a list of children with their parents and the position of the
    // child (in case several children have the same parent
    List respList = new ArrayList(shusize);
    List parentList = new ArrayList(shusize);
    int[] posList = new int[shusize];
    int j = 0;

    for (Iterator responses = el_labels.iterator(); responses.hasNext();) {
      Element response = (Element) responses.next();
      Element parent = response.getParent();
      int pos = parent.indexOf(response);
      posList[j++] = pos;
      respList.add(response.clone()); // need to use clones so they are not
      // attached anymore
      parentList.add(parent);
    }
    Collections.shuffle(respList);
    // put the children back to the parents
    for (int i = 0; i < parentList.size(); i++) {
      Element parent = (Element) parentList.get(i);
      int pos = posList[i];
      Element child = (Element) respList.get(i);
      parent.elements().set(pos, child);
    }
    return shuffleItem;
  }

  /**
   * @return Variables
   */
  public Variables getVariables() {
    return variables;
  }

 
  /**
   * @return
   */
  public Output getOutput() {
    return output;
  }

  /**
   * @return ItemInput
   */
  public ItemInput getItemInput() {
    return itemInput;
  }

  /**
   * Sets the itemInput.
   * @param theItemInput The itemInput to set. max be null for "unanswered"
   * @return the status of the add operation, e.g. ok
   */
  public int addItemInput(ItemInput theItemInput) {
    boolean underMax = isUnderMaxAttempts();
    boolean onTime = isOnTime();
    if (underMax && onTime) {
      start();
      timesAnswered++;
      latestAnswerTime = System.currentTimeMillis();
      this.itemInput = theItemInput;
      evalNeeded = true;
      return QTIConstants.ITEM_SUBMITTED;
    } else {
      if (!underMax) return QTIConstants.ERROR_SUBMITTEDITEM_TOOMANYATTEMPTS;
      else return QTIConstants.ERROR_SUBMITTEDITEM_OUTOFTIME;
    }
  }

  /**
   * @return int
   */
  public int getTimesAnswered() {
    return timesAnswered;
  }

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

  /**
   * @see java.lang.Object#toString()
   */
  public String toString() {
    return "item:" + getIdent() + ":inp:" + itemInput + ",vars:" + variables + ",out:" + output + "=" + super.toString();
  }

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

  /**
   * @return
   */
  public boolean isOpen() {
    // open when in timelimit or not started yet or no timelimit AND maxattempts
    // undef or attempts < maxattempts
    boolean ok = isOnTime() && isUnderMaxAttempts();
    return ok;
  }

  /**
   * @return
   */
  public boolean isOnTime() {
    return (timeOfStart == -1) || (durationLimit == -1) || (System.currentTimeMillis() < timeOfStart + durationLimit);
  }

  /**
   * @return
   */
  public boolean isUnderMaxAttempts() {
    return (maxAttempts == -1 || timesAnswered < maxAttempts);
  }

  /**
   * @return Element
   */
  public Element getEl_item() {
    return el_shuffled_item;
  }

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

  /**
   * return duration for answered items
   * @return
   */
  public long getTimeSpent() {
    if (timesAnswered == 0) return -1;
    return latestAnswerTime - timeOfStart;
  }

  /**
   * Returns the duration.
   *
   * @return long
   */
  public long getDurationLimit() {
    return durationLimit;
  }

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

  /**
   * @return int
   */
  public int getMaxAttempts() {
    return maxAttempts;
  }

  /**
   * @return long
   */
  public long getLatestAnswerTime() {
    return latestAnswerTime;
  }

  /**
   *
   */
  public void eval() {
    if (assessInstance.isSurvey()) return;
    if (evalNeeded) {
      QTI_item qtiItem = QTIHelper.getQtiItem();
      output = new Output(); // clear any previous feedback, hints, solutions
      qtiItem.evalAnswer(this);
      evalNeeded = false;
    }
  }

  /**
   * @return
   */
  public float getMaxScore() {
    Variable var = getVariables().getSCOREVariable();
    return var.getMaxValue();
  }

  /**
   * Returns the value of the SCORE variable
   *
   * @return
   */
  public float getScore() {
    Variable var = getVariables().getSCOREVariable();
    if (var == null) {
      // we demand that a SCORE variable must always exist
      throw new RuntimeException("no SCORE def for " + getIdent());
    } else {
      float sc = var.getTruncatedValue();
      return sc;
    }
  }

  /**
   * @return boolean
   */
  public boolean isFeedback() {
    return feedback;
  }

  /**
   * @return boolean
   */
  public boolean isHints() {
    return hints;
  }

  /**
   * @return boolean
   */
  public boolean isSolutions() {
    return solutions;
  }

  /**
   *
   */
  public void resetVariables() {
    Element el_outcomes = (Element) getEl_item().selectSingleNode("resprocessing/outcomes");
    variables = QTIHelper.declareVariables(el_outcomes);
  }

  /**
   * @return int
   */
  public int getHintLevel() {
    return hintLevel;
  }

  /**
   * Sets the hintLevel.
   *
   * @param hintLevel The hintLevel to set
   */
  public void setHintLevel(int hintLevel) {
    this.hintLevel = hintLevel;
  }

  /**
   *
   */
  public void clearDurationLimit() {
    durationLimit = -1;
  }

  /**
   * @return item
   */
  public Item getQtiItem() {
    return qtiItem;
  }
}
TOP

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

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.