Coder Social home page Coder Social logo

oat-sa / qti-sdk Goto Github PK

View Code? Open in Web Editor NEW
79.0 54.0 32.0 16.37 MB

A QTI (Question & Test Interoperability) Software Development Kit for PHP

Home Page: http://www.taotesting.com

License: GNU General Public License v2.0

PHP 98.62% CSS 0.84% HTML 0.53%
computer-based-assessment php ims-qti qti-specification qti-sdk test-interoperability qti-20 qti-21 qti question-test-interoperability

qti-sdk's Introduction

QTI-SDK

Latest Version Coverage Status License GPL2 Packagist Downloads

QTI Software Development Kit for PHP

An IMS QTI (Question & Test Interoperability) Software Development Kit for PHP 7.0 and higher supporting a wide range of features described by the IMS QTI specification family.

This implementation of QTI is under constant enhancement. The API of the master branch might change at any time.

Features

  • Targets QTI 2.0, 2.1 and partially 2.2
  • Complete QTI Information Model
  • Complete QTI Rule Engine Support
  • Custom Operator Hooks through PSR-0/PSR-4
  • Wilbert Kraan's / Steve Lay's Goldilocks Rendering
  • CSS Parser for direct QTI Information Model mapping at rendering time
  • Item and Test Sessions (with lightning fast binary persistence)
  • Nice and Clean API for QTI Document manipulation/traversal
  • PreConditions & Branching
  • Selection and Ordering
  • Response, Outcome and Template Processing
  • aria-* attributes
  • Unit test driven

Installation (developers)

  1. Clone the repository.
  2. Make sure you know how Composer works and it is installed on your system.
  3. php composer.phar install
  4. You are ready!

Unit Tests (developers)

Run Unit Tests by invoking the following shell command:

cp phpunit.xml.dist phpunit.xml
./vendor/bin/phpunit test

Contribute

We are always looking for people to feed the project with:

  • Bug reports
  • Unit tests
  • New features

Please make yourself known!

QTI Item Session Management

Introduction Example

The following example demonstrates how to instantiate an item session for a given QTI XML item document. The item in use in this example is the "Composition of Water" item, from the QTI 2.1 Implementation Guide.

<?php
use qtism\common\enums\BaseType;
use qtism\common\enums\Cardinality;
use qtism\common\datatypes\QtiIdentifier;
use qtism\data\storage\xml\XmlDocument;
use qtism\runtime\common\State;
use qtism\runtime\common\ResponseVariable;
use qtism\runtime\common\MultipleContainer;
use qtism\runtime\tests\AssessmentItemSession;

// Instantiate a new QTI XML document, and load a QTI XML document.
$itemDoc = new XmlDocument('2.1');
$itemDoc->load('choice_multiple.xml');

/* 
 * A QTI XML document can be used to load various pieces of QTI content such as assessmentItem,
 * assessmentTest, responseProcessing, ... components. Our target is an assessmentItem, which is the
 * root component of our document.
 */
$item = $itemDoc->getDocumentComponent();

/* 
 * The item session represents the collected and computed data related to the interactions a
 * candidate performs on a single assessmentItem. As per the QTI specification, "an item session
 * is the accumulation of all attempts at a particular instance of an assessmentItem made by
 * a candidate.
 */
$itemSession = new AssessmentItemSession($item);

// The candidate is entering the item session, and is beginning his first attempt.
$itemSession->beginItemSession();
$itemSession->beginAttempt();

/* 
 * We instantiate the responses provided by the candidate for this assessmentItem, for the current
 * item session. For this assessmentItem, the data collected from the candidate is represented by 
 * a State, composed of a single ResponseVariable named 'RESPONSE'.
 */
$responses = new State(
    array(
        // The 'RESPONSE' ResponseVariable has a QTI multiple cardinality, and a QTI identifier baseType.
        new ResponseVariable(
            'RESPONSE',
            Cardinality::MULTIPLE,
            BaseType::IDENTIFIER,
            /* 
             * The ResponseVariable value is a Container with multiple cardinality and an identifier
             * baseType to meet the cardinality and baseType requirements of the ResponseVariable.
             */
            new MultipleContainer(
                BaseType::IDENTIFIER,
                /*
                 * The values composing the Container are identifiers 'H' and 'O', which represent
                 * the correct response to this item.
                 */
                array(
                    new QtiIdentifier('H'),
                    new QtiIdentifier('O')
                )
            )
        )
    )
);

/*
 * The candidate is finishing the current attempt, by providing a correct response.
 * ResponseProcessing takes place to produce a new value for the 'SCORE' OutcomeVariable.
 */
$itemSession->endAttempt($responses);

// The item session variables and their values can be accessed by their identifier.
echo 'numAttempts: ' . $itemSession['numAttempts'] . "\n";
echo 'completionStatus: ' . $itemSession['completionStatus'] . "\n";
echo 'RESPONSE: ' . $itemSession['RESPONSE'] . "\n";
echo 'SCORE: ' . $itemSession['SCORE'] . "\n";

/*
 * numAttempts: 1
 * completionStatus: completed
 * RESPONSE: ['H'; 'O']
 * SCORE: 2
 */

// End the current item session.
$itemSession->endItemSession();

Multiple Attempts Example

As per the QTI specification, item sessions allow a single attempt to be performed by default. Trying to begin an attempt that will make the item session exceeding the maximum number of attempts will lead to a PHP exception, as in the following example.

<?php
use qtism\data\storage\xml\XmlDocument;
use qtism\runtime\common\State;
use qtism\runtime\tests\AssessmentItemSession;
use qtism\runtime\tests\AssessmentItemSessionException;

$itemDoc = new XmlDocument('2.1');
$itemDoc->load('choice_multiple.xml');
$item = $itemDoc->getDocumentComponent();

$itemSession = new AssessmentItemSession($item);
$itemSession->beginItemSession();

// Begin 1st attempt.
$itemSession->beginAttempt();
// End attempt by providing an empty response...
$itemSession->endAttempt(new State());

// Begin 2nd attempt, but by default, maximum number of attempts is 1.
try {
    $itemSession->beginAttempt();
} catch (AssessmentItemSessionException $e) {
    echo $e->getMessage();
    // A new attempt for item 'choiceMultiple' is not allowed. The maximum number of attempts (1) is reached.
}

If multiple attempts are permitted on a given assessmentItem, the itemSessionControl's maxAttempts attribute can be modified to allow multiple or unlimited attempts that can be performed by a candidate.

<?php
use qtism\data\ItemSessionControl;
use qtism\data\storage\xml\XmlDocument;
use qtism\runtime\common\State;
use qtism\runtime\tests\AssessmentItemSession;

$itemDoc = new XmlDocument('2.1');
$itemDoc->load('choice_multiple.xml');
$item = $itemDoc->getDocumentComponent();
$itemSession = new AssessmentItemSession($item);

// Set the maximum number of attempts to 0 (means unlimited).
$itemSessionControl = new ItemSessionControl();
$itemSessionControl->setMaxAttempts(0);
$itemSession->setItemSessionControl($itemSessionControl);

// Performing multiple attempts will not lead to a PHP exception anymore, because the maximum number of attemps is unlimited!
$itemSession->beginItemSession();

// 1st attempt will be an incorrect response from the candidate (['H'; 'Cl']).
$responses = new State(
    array(
        new ResponseVariable(
            'RESPONSE',
            Cardinality::MULTIPLE,
            BaseType::IDENTIFIER,
            new MultipleContainer(
                BaseType::IDENTIFIER,
                array(
                    new QtiIdentifier('H'),
                    new QtiIdentifier('Cl')
                )
            )
        )
    )
);

$itemSession->endAttempt($responses);

echo 'numAttempts: ' . $itemSession['numAttempts'] . "\n";
echo 'completionStatus: ' . $itemSession['completionStatus'] . "\n";
echo 'RESPONSE: ' . $itemSession['RESPONSE'] . "\n";
echo 'SCORE: ' . $itemSession['SCORE'] . "\n";

/*
 * numAttempts: 1
 * completionStatus: completed
 * RESPONSE: ['H'; 'N']
 * SCORE: 0
 */

// 2nd attempt will send a correct response this time (['H'; 'O'])!
$itemSession->beginAttempt();
$responses['RESPONSE'][1]->setValue('O');
$itemSession->endAttempt();

echo 'numAttempts: ' . $itemSession['numAttempts'] . "\n";
echo 'completionStatus: ' . $itemSession['completionStatus'] . "\n";
echo 'RESPONSE: ' . $itemSession['RESPONSE'] . "\n";
echo 'SCORE: ' . $itemSession['SCORE'] . "\n";

/*
 * numAttempts: 2
 * completionStatus: completed
 * RESPONSE: ['H'; 'O']
 * SCORE: 2
 */

$itemSession->endItemSession();

You can get more information on the QTI-SDK GitHub Wiki!

QTI Rendering

The QTI Software Development Kit enables you to transform XML serialized QTI files into their (X)HTML5 Goldilocks equivalent. The following shell command renders the path/to/qti.xml QTI file into an HTML5 document using the (X)HTML5 Golidlocks rendering flavour with indentation formatting. The rendering output (stdout) is redirected to the /home/jerome/qti.html file.

./vendor/bin/qtisdk render -df --source path/to/qti.xml --flavour goldilocks > /home/jerome/qti.html

For additional help and information, just call the help screen to know about the features provided by the rendering binaries!

./vendor/bin/qtisdk render --help

Configuration

As for other major PHP frameworks such as Doctrine QTI-SDK makes use of annotations. In such a context, the two following Zend Opcache configuration directives must be configured as below.

qti-sdk's People

Contributors

antoinerobin avatar augustas avatar bartlomiejmarszal avatar boajer avatar bugalot avatar businesspunk avatar edwin-focaloid avatar emgolubev avatar gabrielfs7 avatar ivan-timchur-oat avatar jbout avatar jsconan avatar julien-sebire avatar kilatib avatar m-lagarde avatar mike-ionut-mihai-sandu-tao avatar oat-github-bot avatar oatymart avatar peetya avatar pnal avatar poyuki avatar quintanilhar avatar serpentblade avatar shpran avatar siwane avatar stellalie avatar tverhoof avatar wazelin avatar zagovorichev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qti-sdk's Issues

Object is a reserved word as of PHP 7.2

The Object QTI class located in src\qtism\data\content\xhtml\Object.php conflicts with a reserved word in PHP 7.2. The Content Marshaller class located in src\qtism\data\storage\xml\marshalling\ContentMarshaller.php is at least one file that relies on this class. The naming of that class and use clause prevent the qti-sdk from functioning properly.

Issue when rendering feedback

There is a "bug" or better "not covered case" in the rendering routine in the qtism-sdk.

The problem occurs when the outcomeDeclaration of the feedback has cardinality=multiple (i.e. feedback_adaptive.xml).

The affected file is qtism/qtism/src/qtism/common/collections/Container.php -> public function equals($obj).

In the feedback example above the $obj type is "string" and the workaround is to add an elseif at the line 150:
elseif (gettype($obj) === 'string') {
foreach (array_keys($this->getDataPlaceHolder()) as $key) {
if ($obj === $this[$key]->getValue()) {
return true;
}
}
return false;
}

Would you please so kind to check it and let me know if I misunderstood something.

Kindest regards

Additional documentation or help creating one unmarshalling single node element

Hi,
I'm new to this project i just study the SDK code.

I'm looking for a way to unmarshal a single \DomElement to validate small pices of xml. But while trying I encounter some Exceptions.

This is what i try to accomplish:

$sdkMarshaller = new Qti211MarshallerFactory();
$cradle = new DOMDocument('1.0', 'UTF-8');
$xml    = new \DOMElement('hottextInteraction', '<p>Sponsors of the Olympic Games <hottext identifier="A">who bought</hottext>
				advertising time on United States television <hottext identifier="B">includes</hottext>
				<hottext identifier="C">at least</hottext> a dozen international firms <hottext
					identifier="D">whose</hottext> names are familiar to American consumers.
					<hottext identifier="E">No error.</hottext>
			</p>');
 $cradle->appendChild($xml);
 $xml->setAttribute('maxChoices', 1);
 $xml->setAttribute('responseIdentifier', 'RESPONSE');
 $hottextMarshaller = $sdkMarshaller->createMarshaller($cradle->firstChild);
 $hottextMarshaller->unmarshall($cradle->firstChild);

I receive the exception :
The value 'RESPONSE' for the attribute 'responseIdentifier' for element 'hottextInteraction' is not a valid QTI identifier.

But the original exception message is :
A HottextInteraction object must be composed of at least one BlockStatic object, none given.

I think its starts here : \qtism\data\storage\xml\marshalling\responseIdentifier:47

The QtiComponentCollection is empty, and I can't find the correct way to provide a working instance of this to the marshaller

I hope someone can point me to the right direction

Kind regards,
Andreas

img tags do not appear when converted in HTML

When using the XhtmlRenderingEngine to render this XML in HTML, the img tags disappear.

XML :

<?xml version="1.0" encoding="UTF-8"?>
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p2" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p2 http://www.imsglobal.org/xsd/qti/qtiv2p2/imsqti_v2p2.xsd" identifier="i15931796724230158" title="tableau à double entrée" label="tableau à double entrée" xml:lang="en-US" adaptive="false" timeDependent="false" toolName="TAO" toolVersion="3.3.0-sprint73">
  <responseDeclaration identifier="RESPONSE" cardinality="multiple" baseType="directedPair"/>
  <outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float" normalMaximum="0"/>
  <outcomeDeclaration identifier="MAXSCORE" cardinality="single" baseType="float">
    <defaultValue>
      <value>0</value>
    </defaultValue>
  </outcomeDeclaration>
  <itemBody>
    <div class="grid-row">
      <div class="col-12">
        <matchInteraction responseIdentifier="RESPONSE" shuffle="false" maxAssociations="0" minAssociations="0">
          <prompt>Consigne</prompt>
          <simpleMatchSet>
            <simpleAssociableChoice identifier="choice_1" fixed="false" showHide="show" matchMax="0" matchMin="0">test</simpleAssociableChoice>
            <simpleAssociableChoice identifier="choice_2" fixed="false" showHide="show" matchMax="0" matchMin="0">
              <img src="/api/media-files/dbba3437-b7ba-11ea-87ea-c4c9a5dc6151" alt="8d0db8228088942bc288f9a2740fa8fd.png"/>
            </simpleAssociableChoice>
          </simpleMatchSet>
          <simpleMatchSet>
            <simpleAssociableChoice identifier="choice_3" fixed="false" showHide="show" matchMax="0" matchMin="0">
              <img src="/api/media-files/dbba6d25-b7ba-11ea-87ea-c4c9a5dc6151" alt="8d0db8228088942bc288f9a2740fa8fd.png"/>
            </simpleAssociableChoice>
            <simpleAssociableChoice identifier="choice_4" fixed="false" showHide="show" matchMax="0" matchMin="0">
              <img src="/api/media-files/dbba8074-b7ba-11ea-87ea-c4c9a5dc6151" alt="8d0db8228088942bc288f9a2740fa8fd.png"/>
            </simpleAssociableChoice>
            <simpleAssociableChoice identifier="choice_5" fixed="false" showHide="show" matchMax="0" matchMin="0">
              <img src="/api/media-files/dbbaac06-b7ba-11ea-87ea-c4c9a5dc6151" alt="8d0db8228088942bc288f9a2740fa8fd.png"/>
            </simpleAssociableChoice>
          </simpleMatchSet>
        </matchInteraction>
      </div>
    </div>
  </itemBody>
  <responseProcessing template="http://www.imsglobal.org/question/qti_v2p2/rptemplates/match_correct"/>
</assessmentItem>

HTML :

<div data-identifier="i15931796724230158" data-title="tableau &agrave; double entr&eacute;e" data-label="tableau &agrave; double entr&eacute;e" data-adaptive="false" data-time-dependent="false" class="qti-assessmentItem"><div class="qti-itemBody"><div class="qti-div grid-row">
      <div class="qti-div col-12">
        <div data-response-identifier="RESPONSE" data-shuffle="false" data-max-associations="0" data-min-associations="0" class="qti-matchInteraction"><p class="qti-prompt">Consigne</p><table><tr><th></th><th data-identifier="choice_3" data-fixed="false" data-match-max="0" data-match-min="0" class="qti-simpleAssociableChoice">
              </th><th data-identifier="choice_4" data-fixed="false" data-match-max="0" data-match-min="0" class="qti-simpleAssociableChoice">
              </th><th data-identifier="choice_5" data-fixed="false" data-match-max="0" data-match-min="0" class="qti-simpleAssociableChoice">
              </th></tr><tr><th data-identifier="choice_1" data-fixed="false" data-match-max="0" data-match-min="0" class="qti-simpleAssociableChoice">test</th><td><input type="checkbox"></td><td><input type="checkbox"></td><td><input type="checkbox"></td></tr><tr><th data-identifier="choice_2" data-fixed="false" data-match-max="0" data-match-min="0" class="qti-simpleAssociableChoice">
              </th><td><input type="checkbox"></td><td><input type="checkbox"></td><td><input type="checkbox"></td></tr></table></div>
      </div>
    </div></div></div>

Duplicated class names causing resolve class error

In following files the co-existent of class 'Utils' in current and parent directory sometimes cause class resolve errors:

/qti-sdk/qtism/runtime/expressions/operators/EqualProcessor.php
/qti-sdk/qtism/runtime/expressions/operators/EqualRoundedProcessor.php
/qti-sdk/qtism/runtime/expressions/operators/AnyNProcessor.php

namespace qtism\runtime\expressions\operators;
......
use qtism\runtime\expressions\Utils;

Can import the other 'Utils' with another alias to prevent this?

getting the outcome value for each individual interaction

Hi,

Is it possible to get the outcome value for each individual interaction, instead of getting a total sum of the SCORE, without splitting the outcome declaration into multiples?
The isCorrect() of ResponseVariable is not useful in this case since it compares the correct response only.
Thanks

<p> inside <prompt> not comply to QTI ?

Hi,

I used the lastest TAO 3.1.0-rc7 to create a choice interaction and verify the QTI at
https://webapps.ph.ed.ac.uk/qtiworks/anonymous/validator
and it says "Illegal child with QTI class name p for parent prompt". I checked the QTI specification and it seems the prompt element only contains inlineStatic elements which do not include p tag. The qti-sdk also do not show any error with that.

There are a few other QTI compliance issues I dont know if qti-sdk should validate it or not:

  • default reponse processing template when interaction identifier is not 'RESPONSE' (problem with tao 3.0?)
  • src attribute should be URI encoded
  • invalid element attributes (e.g. style, table width, etc.)

Adrian

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.