Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick de Klein committed Feb 13, 2025
2 parents cfc6f76 + 4fd1ea6 commit d201668
Show file tree
Hide file tree
Showing 37 changed files with 1,243 additions and 363 deletions.
34 changes: 34 additions & 0 deletions public/assets/qti-item/example-choice-nocorrect-item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<qti-assessment-item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0"
xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd"
identifier="ITM-choice" title="Unattended Luggage" adaptive="false" time-dependent="false">
<qti-response-declaration identifier="RESPONSE" cardinality="single" base-type="identifier">
</qti-response-declaration>
<qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float">
<qti-default-value>
<qti-value>0</qti-value>
</qti-default-value>
</qti-outcome-declaration>

<qti-outcome-declaration identifier="MAXSCORE" cardinality="single" base-type="float">
<qti-default-value>
<qti-value>1</qti-value>
</qti-default-value>
</qti-outcome-declaration>
<qti-item-body>
<p>Look at the text in the picture.</p>
<p>
<img src="./img/sign.png" alt="NEVER LEAVE LUGGAGE UNATTENDED" />
</p>
<qti-choice-interaction response-identifier="RESPONSE" shuffle="false" max-choices="1">
<qti-prompt>What does it say?</qti-prompt>
<qti-simple-choice identifier="ChoiceA">You must stay with your luggage at all times.</qti-simple-choice>
<qti-simple-choice identifier="ChoiceB">Do not let someone else look after your luggage.</qti-simple-choice>
<qti-simple-choice identifier="ChoiceC">Remember your luggage when you leave.</qti-simple-choice>
</qti-choice-interaction>
</qti-item-body>
<qti-response-processing
template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct.xml" />
</qti-assessment-item>
44 changes: 44 additions & 0 deletions public/assets/qti-item/example-graphic-order.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<qti-assessment-item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0"
xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd"
identifier="i677d35643d0b320250107150836b4f2" title="Item 2" label="Graphic Order Interaction 1"
xml:lang="en-US" adaptive="false" time-dependent="false" tool-name="TAO"
tool-version="2024.11 LTS">
<qti-response-declaration identifier="RESPONSE" cardinality="ordered" base-type="identifier">
<qti-correct-response>
<qti-value>hotspot_1</qti-value>
<qti-value>hotspot_3</qti-value>
<qti-value>hotspot_4</qti-value>
<qti-value>hotspot_2</qti-value>
</qti-correct-response>
</qti-response-declaration>
<qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float"
normal-maximum="0" normal-minimum="0" />
<qti-outcome-declaration identifier="MAXSCORE" cardinality="single" base-type="float">
<qti-default-value>
<qti-value>0</qti-value>
</qti-default-value>
</qti-outcome-declaration>
<qti-item-body>
<div class="grid-row">
<div class="col-12">
<qti-graphic-order-interaction response-identifier="RESPONSE" class="responsive">
<qti-prompt>Test</qti-prompt>
<img src="/img/map-us.png" alt="" width="795" height="492" />
<qti-hotspot-choice identifier="hotspot_1" fixed="false" shape="rect"
coords="74,101,234,194" />
<qti-hotspot-choice identifier="hotspot_2" fixed="false" shape="ellipse"
coords="338,259,77,73" />
<qti-hotspot-choice identifier="hotspot_3" fixed="false" shape="ellipse"
coords="444,148,47,46" />
<qti-hotspot-choice identifier="hotspot_4" fixed="false" shape="poly"
coords="114,269,146,295,181,271,154,249" />
</qti-graphic-order-interaction>
</div>
</div>
</qti-item-body>
<qti-response-processing
template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct.xml" />
</qti-assessment-item>
35 changes: 35 additions & 0 deletions public/assets/qti-item/example-select-point.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<qti-assessment-item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0"
xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd"
identifier="i677f91b17e11b20250109100657d9f7" title="Item 2" label="Select point interaction"
xml:lang="en-US" adaptive="false" time-dependent="false" tool-name="TAO"
tool-version="2024.11 LTS">
<qti-response-declaration identifier="RESPONSE" cardinality="multiple" base-type="point">
<qti-correct-response>
<qti-value>152 141</qti-value>
<qti-value>253 220</qti-value>
<qti-value>317 194</qti-value>
</qti-correct-response>
</qti-response-declaration>
<qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float"
normal-maximum="0" normal-minimum="0" />
<qti-outcome-declaration identifier="MAXSCORE" cardinality="single" base-type="float">
<qti-default-value>
<qti-value>0</qti-value>
</qti-default-value>
</qti-outcome-declaration>
<qti-item-body>
<div class="grid-row">
<div class="col-12">
<qti-select-point-interaction response-identifier="RESPONSE" max-choices="0"
min-choices="0" class="responsive">
<img src="/img/map-us.png" alt="map" width="795" height="492" />
</qti-select-point-interaction>
</div>
</div>
</qti-item-body>
<qti-response-processing
template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/map_response_point.xml" />
</qti-assessment-item>
Binary file added public/assets/qti-item/img/map-us.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/lib/exports/computed-item.context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { createContext } from '@lit/context';

import type { VariableDeclaration } from './variables';

export type ComputedItemContext = {
identifier: string;
href?: string;
Expand All @@ -9,7 +11,7 @@ export type ComputedItemContext = {
adaptive?: boolean;
timeDependent?: boolean;
title?: string;
value?: Readonly<string | string[]>;
variables: ReadonlyArray<VariableDeclaration<string | string[] | null>>;
};

export const computedItemContext = createContext<Readonly<ComputedItemContext>>(Symbol('computedItemContext'));
3 changes: 1 addition & 2 deletions src/lib/exports/variables.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { QtiAreaMapping } from '../qti-components/qti-response-processing/qti-expression/qti-area-mapping/qti-area-mapping';
import type { QtiMapping } from '../qti-components/qti-response-processing/qti-expression/qti-mapping/qti-mapping';
import type { QtiAreaMapping, QtiMapping } from '../qti-components';
import type { BaseType, Cardinality } from './expression-result';

export interface VariableValue<T> {
Expand Down
44 changes: 22 additions & 22 deletions src/lib/qti-components/qti-assessment-item/qti-assessment-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,16 @@ export class QtiAssessmentItem extends LitElement {
}
});

get _context(): ItemContext {
get context(): ItemContext {
return this._contextProxy;
}

set _context(value: ItemContext) {
set context(value: ItemContext) {
Object.assign(this._contextProxy, value);
}

public get variables(): VariableValue<string | string[] | null>[] {
return this._context.variables.map(v => ({
return this.context.variables.map(v => ({
identifier: v.identifier,
value: v.value,
type: v.type,
Expand All @@ -88,17 +88,17 @@ export class QtiAssessmentItem extends LitElement {
console.warn('variables property should be an array of VariableDeclaration');
return;
}
this._context = {
...this._context,
variables: this._context.variables.map(variable => {
this.context = {
...this.context,
variables: this.context.variables.map(variable => {
const matchingValue = value.find(v => v.identifier === variable.identifier);
if (matchingValue) {
return { ...variable, ...matchingValue };
}
return variable;
})
};
this._context.variables.forEach(variable => {
this.context.variables.forEach(variable => {
if (variable.type === 'response') {
const interactionElement = this._interactionElements.find(
(el: Interaction) => el.responseIdentifier === variable.identifier
Expand All @@ -110,7 +110,7 @@ export class QtiAssessmentItem extends LitElement {
});
}

private _initialContext: Readonly<ItemContext> = { ...this._context, variables: this._context.variables };
private _initialContext: Readonly<ItemContext> = { ...this.context, variables: this.context.variables };
private _feedbackElements: QtiFeedback[] = [];
private _interactionElements: Interaction[] = [];

Expand Down Expand Up @@ -146,15 +146,15 @@ export class QtiAssessmentItem extends LitElement {
constructor() {
super();
this.addEventListener('qti-register-variable', (e: QtiRegisterVariable) => {
this._context = { ...this._context, variables: [...this._context.variables, e.detail.variable] };
this._initialContext = this._context;
this.context = { ...this.context, variables: [...this.context.variables, e.detail.variable] };
this._initialContext = this.context;
e.stopPropagation();
});
this.addEventListener('qti-register-feedback', (e: CustomEvent<QtiFeedback>) => {
e.stopPropagation();
const feedbackElement = e.detail;
this._feedbackElements.push(feedbackElement);
const numAttempts = Number(this._context.variables.find(v => v.identifier === 'numAttempts')?.value) || 0;
const numAttempts = Number(this.context.variables.find(v => v.identifier === 'numAttempts')?.value) || 0;
if (numAttempts > 0) {
feedbackElement.checkShowFeedback(feedbackElement.outcomeIdentifier);
}
Expand Down Expand Up @@ -194,7 +194,7 @@ export class QtiAssessmentItem extends LitElement {
public showCorrectResponse(show: boolean): void {
// Process response variables with correct responses.
// Update interactions with the correct responses or clear them.
for (const responseVariable of this._context.variables.filter(v => v.type === 'response') as ResponseVariable[]) {
for (const responseVariable of this.context.variables.filter(v => v.type === 'response') as ResponseVariable[]) {
const interaction = this._interactionElements.find(
element => element.getAttribute('response-identifier') === responseVariable.identifier
);
Expand Down Expand Up @@ -228,7 +228,7 @@ export class QtiAssessmentItem extends LitElement {
if (countNumAttempts) {
this.updateOutcomeVariable(
'numAttempts',
(+this._context.variables.find(v => v.identifier === 'numAttempts')?.value + 1).toString()
(+this.context.variables.find(v => v.identifier === 'numAttempts')?.value + 1).toString()
);
}

Expand All @@ -237,7 +237,7 @@ export class QtiAssessmentItem extends LitElement {
}

public resetResponses() {
this._context = this._initialContext;
this.context = this._initialContext;
}

public getResponse(identifier: string): Readonly<ResponseVariable> {
Expand All @@ -249,7 +249,7 @@ export class QtiAssessmentItem extends LitElement {
}

public getVariable(identifier: string): Readonly<VariableDeclaration<string | string[] | null>> {
return this._context.variables.find(v => v.identifier === identifier) || null;
return this.context.variables.find(v => v.identifier === identifier) || null;
}

// saving privates here: ------------------------------------------------------------------------------
Expand All @@ -260,9 +260,9 @@ export class QtiAssessmentItem extends LitElement {
}

public updateResponseVariable(identifier: string, value: string | string[] | undefined) {
this._context = {
...this._context,
variables: this._context.variables.map(v => (v.identifier !== identifier ? v : { ...v, value: value }))
this.context = {
...this.context,
variables: this.context.variables.map(v => (v.identifier !== identifier ? v : { ...v, value: value }))
};

this._emit<InteractionChangedDetails>('qti-interaction-changed', {
Expand All @@ -285,9 +285,9 @@ export class QtiAssessmentItem extends LitElement {
return;
}

this._context = {
...this._context,
variables: this._context.variables.map(v => {
this.context = {
...this.context,
variables: this.context.variables.map(v => {
if (v.identifier !== identifier) {
return v;
}
Expand All @@ -302,7 +302,7 @@ export class QtiAssessmentItem extends LitElement {
this._emit<OutcomeChangedDetails>('qti-outcome-changed', {
item: this.identifier,
outcomeIdentifier: identifier,
value: this._context.variables.find(v => v.identifier === identifier)?.value
value: this.context.variables.find(v => v.identifier === identifier)?.value
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { property, query } from 'lit/decorators.js';

import { watch } from '../../../../decorators/watch';

import type { ResponseVariable } from '../../../../exports/variables';
import type { ChoiceInterface } from '../active-element/active-element.mixin';
import type { Interaction } from '../../../../exports/interaction';
import type { IInteraction } from '../../../../exports/interaction.interface';
Expand All @@ -10,9 +11,7 @@ type Constructor<T = {}> = abstract new (...args: any[]) => T;

export type Choice = HTMLElement & ChoiceInterface & { internals: ElementInternals };

export interface ChoicesInterface extends IInteraction {
correctResponse: string | string[];
}
export interface ChoicesInterface extends IInteraction {}

export const ChoicesMixin = <T extends Constructor<Interaction>>(superClass: T, selector: string) => {
abstract class ChoicesMixinElement extends superClass implements ChoicesInterface {
Expand Down Expand Up @@ -73,20 +72,39 @@ export const ChoicesMixin = <T extends Constructor<Interaction>>(superClass: T,
this._updateChoiceSelection();
}

public set correctResponse(value: string | string[]) {
this._correctResponse = value;
const responseArray = Array.isArray(value) ? value : [value];
this._choiceElements.forEach(choice => {
choice.internals.states.delete('correct-response');
choice.internals.states.delete('incorrect-response');
if (responseArray.length > 0) {
if (responseArray.includes(choice.identifier)) {
choice.internals.states.add('correct-response');
} else {
choice.internals.states.add('incorrect-response');
// public set correctResponse(value: string | string[]) {
// this._correctResponse = value;
// const responseArray = Array.isArray(value) ? value : [value];
// this._choiceElements.forEach(choice => {
// choice.internals.states.delete('correct-response');
// choice.internals.states.delete('incorrect-response');
// if (responseArray.length > 0) {
// if (responseArray.includes(choice.identifier)) {
// choice.internals.states.add('correct-response');
// } else {
// choice.internals.states.add('incorrect-response');
// }
// }
// });
// }

public toggleCorrectResponse(responseVariable: ResponseVariable, show: boolean) {
if (responseVariable.correctResponse) {
const responseArray = Array.isArray(responseVariable.correctResponse)
? responseVariable.correctResponse
: [responseVariable.correctResponse];
this._choiceElements.forEach(choice => {
choice.internals.states.delete('correct-response');
choice.internals.states.delete('incorrect-response');
if (show && responseArray.length > 0) {
if (responseArray.includes(choice.identifier)) {
choice.internals.states.add('correct-response');
} else {
choice.internals.states.add('incorrect-response');
}
}
}
});
});
}
}

override connectedCallback() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default css`
line-container {
display: block;
position: relative;
width: fit-content;
}
svg {
position: absolute;
Expand Down
Loading

0 comments on commit d201668

Please sign in to comment.