Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: integrate complex-rubric; deprecate rubric and multi-trait-rubric #115

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
import {
ItemConfig,
ItemSession,
PieContent,
PieController,
PieElement,
PieModel,
Expand All @@ -28,11 +27,12 @@ import {
export namespace Components {
interface PieAuthor {
/**
* Utility method to add a `@pie-element/multi-trait-rubric` section to an item config when creating an item should be used before setting the config. *
* Utility method to add a `@pie-element/multi-trait-rubric` section to an item config when creating an item should be used before setting the config.
* @deprecated this method was for temporary use, was removed in the latest major release *
* @param config the item config to mutate
* @param multiTraitRubricModel
*/
'addMultiTraitRubricToConfig': (config: ItemConfig, multiTraitRubricModel?: any) => Promise<PieContent>;
'addMultiTraitRubricToConfig': (config: ItemConfig, multiTraitRubricModel?: any) => Promise<ItemConfig>;
/**
* Adds a preview view which will render the content in another tab as it may appear to a student or instructor.
*/
Expand All @@ -43,11 +43,11 @@ export namespace Components {
'addRubric': boolean;
/**
* Utility method to add a `@pie-element/rubric` section to an item config when creating an item should be used before setting the config.
* @deprecated this method is for temporary use, will be removed at next major release
* @deprecated this method was for temporary use, was removed in the latest major release
* @param config the item config to mutate
* @param rubricModel
*/
'addRubricToConfig': (config: ItemConfig, rubricModel?: any) => Promise<PieContent>;
'addRubricToConfig': (config: ItemConfig, rubricModel?: any) => Promise<ItemConfig>;
/**
* Provide this property override the default endpoints used by the player to retrieve JS bundles. Must be set before setting the config property. Most users will not need to use this property.
*/
Expand Down
218 changes: 157 additions & 61 deletions src/components/pie-author/pie-author.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {pieContentFromConfig} from "../../utils/utils";
import parseNpm from "parse-package-name";
import _isEqual from "lodash/isEqual";
import _isEmpty from "lodash/isEmpty";
import {addMultiTraitRubric, addPackageToContent, addRubric} from "../../rubric-utils";
import {addComplexRubric, addPackageToContent} from "../../rubric-utils";

import {
ModelUpdatedEvent,
Expand Down Expand Up @@ -151,7 +151,7 @@ export class Author {
if (!this.pieContentModel || !this.pieContentModel.models) {
console.error('No pie content model');

return { hasErrors: false, validatedModels: {} };
return {hasErrors: false, validatedModels: {}};
}

return (this.pieContentModel.models || []).reduce((acc: any, model) => {
Expand Down Expand Up @@ -199,10 +199,9 @@ export class Author {
}
}
return acc;
}, { hasErrors: false, validatedModels: {} });
}, {hasErrors: false, validatedModels: {}});
}


constructor() {
this.handleFileInputChange = (e: Event) => {
const input = e.target;
Expand Down Expand Up @@ -270,13 +269,146 @@ export class Author {
}
}

removeRubricFromMarkup(rubricElements) {
const tempDiv = this.doc.createElement("div");

tempDiv.innerHTML = this.pieContentModel.markup;

const elsWithId = tempDiv.querySelectorAll("[id]");

elsWithId.forEach(el => {
const pieElName = el.tagName.toLowerCase().split("-config")[0];

if (rubricElements.includes(pieElName)) {
try {
tempDiv.querySelector(`#${el.id}`).remove();
} catch (e) {
console.log(e.toString());
}
}
});

const newMarkup = tempDiv.innerHTML;

tempDiv.remove();

return newMarkup;
}

removeRubricItemTypes(rubricElements) {
if (!rubricElements.length || !this.pieContentModel.models) {
return [];
}

// save the rubric and multi-trait-rubric models that we're going to delete
const deletedModels = this.pieContentModel.models.filter(model => rubricElements.includes(model.element));

// delete the rubric and multi-trait-rubric elements
rubricElements.forEach(rubricElementKey => delete this.pieContentModel.elements[rubricElementKey]);

// delete the rubric and multi-trait-rubric models
this.pieContentModel.models = this.pieContentModel.models.filter(model => !rubricElements.includes(model.element));

// delete the rubric and multi-trait-rubric nodes from markup
this.pieContentModel.markup = this.removeRubricFromMarkup(rubricElements);

return deletedModels;
}

addComplexRubric(complexRubricModel) {
// add complex-rubric
addPackageToContent(
this.pieContentModel,
"@pie-element/complex-rubric",
complexRubricModel as PieModel
);

this.pieContentModel = addComplexRubric(this.pieContentModel);
}

/**
* The main flows for parsing an item config:
*
* When player gets initialized:
* 1. IF has rubric
* A. IF at least one model has withRubric = true => we replace rubric item with complex-rubric item (that will contain old rubric item data)
* B. IF no model has withRubric = true => we remove rubric item from item config
* 2. ELSE IF doesn't have rubric
* A. IF at least one model has withRubric = true => we add a complex-rubric item (that will be empty)
* B. IF no model has withRubric = true => we do nothing
*
* When a model gets updated, we check again:
* 1. IF there's at least one model that has withRubric = true
* A. IF there was a complex-rubric => we do nothing
* B. IF there was no complex-rubric => we add a complex-rubric item (that will be empty)
* 2. IF there's no model that has withRubric = true
* A. IF there was a complex-rubric => we remove complex-rubric
* B. IF there was no complex-rubric => we do nothing
*/
parseComplexRubric() {
// load all rubric and multi-trait-rubric items
const rubricElements = Object.keys(this.pieContentModel.elements).filter(key => this.pieContentModel.elements[key].indexOf('rubric') >= 0 && this.pieContentModel.elements[key].indexOf('complex-rubric') < 0);

// delete all rubric items and store them for later conversion
const deletedModels = this.removeRubricItemTypes(rubricElements);

// TODO replace teacherInstructionsEnabled -> withRubric
const shouldHaveComplexRubric = this.pieContentModel.models.filter(model => model.teacherInstructionsEnabled).length;
const hasComplexRubric = Object.keys(this.pieContentModel.elements).filter(key => this.pieContentModel.elements[key].indexOf('complex-rubric') >= 0).length;

const baseComplexRubricModel = {
id: "complex-rubric",
element: "pie-complex-rubric",
};

if (shouldHaveComplexRubric) {
if (!deletedModels.length) {
// if should have complex rubric and there was no rubric, just add one from scratch
this.addComplexRubric(baseComplexRubricModel);
} else {
// if should have complex rubric and there were rubric items, use them to add the new complex-rubric
// TODO should we have support for multi rubrics (items/multi-items with more than one rubric item)?
const deletedModel = deletedModels[0];
let type;

// we check what type of rubric item we removed in order to set the type for the new complex-rubric
if (deletedModel.element.indexOf('multi-trait') >= 0) {
type = 'multiTraitRubric';
} else if (deletedModel.element.indexOf('rubric') >= 0) {
type = 'simpleRubric';
}

// TODO I think there's no need for id and element
delete deletedModel.id;
delete deletedModel.element;

this.addComplexRubric({
...baseComplexRubricModel,
type,
rubrics: {
[type]: deletedModel
}
});
}
} else if (hasComplexRubric) {
// if should not have complex-rubric, but it has, then delete complex-rubric
// load all complex-rubric items
const rubricElements = Object.keys(this.pieContentModel.elements).filter(key => this.pieContentModel.elements[key].indexOf('complex-rubric') >= 0);

this.removeRubricItemTypes(rubricElements);
}

this.pieContentModel = pieContentFromConfig(this.pieContentModel);
}

@Watch("config")
async watchConfig(newValue, oldValue) {
if (newValue && !_isEqual(newValue, oldValue)) {
try {
this.elementsLoaded = false;
this._modelLoadedState = false;
this.pieContentModel = pieContentFromConfig(newValue);
this.parseComplexRubric();
this.addConfigTags(this.pieContentModel);
this.loadPieElements();
} catch (error) {
Expand Down Expand Up @@ -379,9 +511,22 @@ export class Author {
}
});
}

if (this._modelLoadedState) {
this.modelUpdated.emit(this.pieContentModel);
}

// TODO replace teacherInstructionsEnabled -> withRubric
const shouldHaveComplexRubric = this.pieContentModel.models.filter(model => model.teacherInstructionsEnabled).length;
const hasComplexRubric = Object.keys(this.pieContentModel.elements).filter(key => this.pieContentModel.elements[key].indexOf('complex-rubric') >= 0).length;

if ((shouldHaveComplexRubric && !hasComplexRubric)
|| (!shouldHaveComplexRubric && hasComplexRubric)) {
this.parseComplexRubric();

// TODO this causes reloading the entire element; can we load only the new item type?
this.watchConfig(this.pieContentModel, {});
}
});

this.el.addEventListener(InsertImageEvent.TYPE, this.handleInsertImage);
Expand Down Expand Up @@ -455,78 +600,29 @@ export class Author {

/**
* Utility method to add a `@pie-element/rubric` section to an item config when creating an item should be used before setting the config.
*
* @deprecated this method is for temporary use, will be removed at next major release
*
* @deprecated this method was for temporary use, was removed in the latest major release
* @param config the item config to mutate
* @param rubricModel
*/
@Method()
async addRubricToConfig(config: ItemConfig, rubricModel?) {
if (!rubricModel) {
rubricModel = {
id: "rubric",
element: "pie-rubric",
points: ["", "", "", ""],
maxPoints: 4,
excludeZero: false
};
}
const configPieContent = pieContentFromConfig(config);
addPackageToContent(
configPieContent,
"@pie-element/rubric",
rubricModel as PieModel
);
return addRubric(configPieContent);
console.error('addRubricToConfig method was for temporary use, so it was removed in the latest major release');

return config;
}

/**
* Utility method to add a `@pie-element/multi-trait-rubric` section to an item config when creating an item should be used before setting the config.
* @deprecated this method was for temporary use, was removed in the latest major release
**
* @param config the item config to mutate
* @param multiTraitRubricModel
*/
@Method()
async addMultiTraitRubricToConfig(config: ItemConfig, multiTraitRubricModel?) {
if (!multiTraitRubricModel) {
multiTraitRubricModel = {
id: "multi-trait-rubric",
element: "pie-multi-trait-rubric",
visibleToStudent: true,
halfScoring: false,
excludeZero: true,
pointLabels: true,
description: false,
standards: false,
scales: [
{
maxPoints: 4,
scorePointsLabels: ['', '', '', ''],
traitLabel: 'Trait',
traits: [
{
name: '',
standards: [],
description: '',
scorePointsDescriptors: [
'',
'',
'',
'',
'',
],
},]
}]
}
}
const configPieContent = pieContentFromConfig(config);
addPackageToContent(
configPieContent,
"@pie-element/multi-trait-rubric",
multiTraitRubricModel as PieModel
);
return addMultiTraitRubric(configPieContent);
console.error('addMultiTraitRubricToConfig method was for temporary use, so it was removed in the latest major release');

return config;
}

render() {
Expand Down
3 changes: 2 additions & 1 deletion src/demo/author-preview-add-rubric.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
elements: {
'pie-multiple-choice': '@pie-element/multiple-choice@latest'
},
models: [MC_model],
// TODO replace teacherInstructionsEnabled -> withRubric
models: [{ ...MC_model, teacherInstructionsEnabled: true }],
markup: "<pie-multiple-choice id='1'></pie-multiple-choice>"
};

Expand Down
33 changes: 28 additions & 5 deletions src/demo/multi-item-rubric.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,26 @@
"height": "200px",
"showMathInput": false,
"width": "500px",
"prompt": "<p>A bag contains 7 red cubes, 8 green cubes, 3 yellow cubes, 5 blue cubes, and 2 white cubes. All of the cubes are exactly the same size.</p>\r\n<p>A. Create a table that represents the contents of the bag and shows the probability of choosing each color when a cube is drawn from the bag without looking. Express the probabilities as fractions.</p>\r\n<p></p>"
"prompt": "<p>A bag contains 7 red cubes, 8 green cubes, 3 yellow cubes, 5 blue cubes, and 2 white cubes. All of the cubes are exactly the same size.</p>\r\n<p>A. Create a table that represents the contents of the bag and shows the probability of choosing each color when a cube is drawn from the bag without looking. Express the probabilities as fractions.</p>\r\n<p></p>",
teacherInstructionsEnabled: false
},
{
"id": "4028e4a23e1a7e7c013e28b6d228132d",
"element": "extended-text-entry",
"height": "200px",
"showMathInput": false,
"width": "500px",
"prompt": "<p>B. Determine the probability of randomly drawing a green cube, returning it to the bag, and then randomly drawing a blue cube.</p>\r\n<ul>\r\n <li>Explain how you can use your table to find the probability.</li>\r\n <li>Write and solve an equation that represents the calculation of the probability.</li>\r\n</ul>\r\n<p></p>"
"prompt": "<p>B. Determine the probability of randomly drawing a green cube, returning it to the bag, and then randomly drawing a blue cube.</p>\r\n<ul>\r\n <li>Explain how you can use your table to find the probability.</li>\r\n <li>Write and solve an equation that represents the calculation of the probability.</li>\r\n</ul>\r\n<p></p>",
teacherInstructionsEnabled: true
},
{
"width": "500px",
"prompt": "<p>C. Explain whether it is more likely or less likely for the event described in part B to occur when the first cube is <span class=\"relative-emphasis\">not</span> returned to the bag before drawing the second. Use words, numbers, and/or diagrams to support your answer.</p>\r\n<p></p>",
"id": "4028e4a23e1a7e7c013e28bca5c91332",
"element": "extended-text-entry",
"height": "200px",
"showMathInput": false
"showMathInput": false,
teacherInstructionsEnabled: false
},
{
"points": [
Expand All @@ -55,12 +58,32 @@
"maxPoints": 4,
"excludeZero": false,
"element": "pie-rubric"
}
},
// {
// "rubrics": {
// "simpleRubric": {
// "points": [
// "<p>The response is completely incorrect, there is no response, or the response is off topic.</p>",
// "<p>The response demonstrates minimal understanding. A level 1 response is characterized by:</p>\r\n<ul>\r\n <li>A table in part A that exhibits up to 3 incorrect probabilities;</li>\r\n <li>An explanation and an equation for part B that are incorrect, incomplete, or missing;</li>\r\n <li>An explanation for part C that is incorrect, incomplete, or missing.</li>\r\n</ul>",
// "<p>The response demonstrates a basic but incomplete understanding. A level 2 response is characterized by:</p>\r\n<ul>\r\n <li>A table in part A that exhibits 1&#8211;2 incorrect probabilities;</li>\r\n <li>An explanation for part B that is vague, incomplete, or missing;</li>\r\n <li>An expression or equation for part B that is correctly derived from the table in part A but may be incorrectly solved;</li>\r\n <li>An explanation for part C that is incomplete.</li>\r\n</ul>",
// "<p>The response demonstrates a strong understanding, but the work contains minor errors. A level 3 response is characterized by:</p>\r\n<ul>\r\n <li>A correctly drawn table in part A that may exhibit minor errors in labeling or organization;</li>\r\n <li>An explanation for part B that contains minor flaws or is incomplete but correct;</li>\r\n <li>An equation for part B that is solvable but may contain a minor flaw leading to an incorrect result;</li>\r\n <li>An explanation in part C that indicates the event is more likely without replacement but may be incomplete or show inadequate supporting details OR leads to a well-supported conclusion that is consistent with calculation errors made in part B.</li>\r\n</ul>",
// "<p>The response demonstrates a high level of understanding. A level 4 response is characterized by:</p>\r\n<ul>\r\n <li>A correctly created and labeled table in part A, with probabilities expressed as fractions, similar to:</li>\r\n</ul>\r\n<p><img src=\"https://localhost:8443/ia/image/13d1a42cd5044dcab77b163343755157\" id=\"13d1a42cd5044dcab77b163343755157\" alt=\"image 13d1a42cd5044dcab77b163343755157\" /></p>\r\n<p>(Accept an unsimplified fraction (5/25) for the probability of Blue);</p>\r\n<ul>\r\n <li>A correct explanation for part B indicating that the compound probability can be calculated as the product of the individual probabilities, similar to \"For the green cube you use the fraction 8/25 from the table, and for the blue cube you use the fraction 1/5 from the table. Then you multiply them together to get 8/125\";</li>\r\n <li>A correct equation in part B, similar to 8/25&#160;&#215; 1/5 = <span class=\"variable\">p</span>, solved as 8/125;</li>\r\n <li>A correct explanation with supporting work for part C indicating that the event is more likely when the green cube is not replaced. Justification should specify that without replacing the green cube the probability for the blue cube to be drawn second increases from 1/5 to 5/24; that the fraction 5/24 is greater than 1/5; and that there are fewer cubes in the box after the green cube is removed, so all cubes have a greater likelihood of being chosen on the second draw, or that the compound probability without replacing the green cube is 1/15, which is greater than 8/125. Accept correct models, such as tree diagrams or tables that clearly illustrate the increased likelihood and/or a clear comparison of equations and solutions representing the two events.</li>\r\n</ul>\r\n<p></p>"
// ],
// "maxPoints": 4,
// "excludeZero": false
// },
// "multiTraitRubric": {}
// },
// "id": "p-4006dac7",
// "element": "pp-pie-element-complex-rubric",
// "type": "simpleRubric"
// }
],
"markup": "<extended-text-entry id=\"4028e4a23e1a7e7c013e28b2be2d1326\"></extended-text-entry><extended-text-entry id=\"4028e4a23e1a7e7c013e28b6d228132d\"></extended-text-entry><extended-text-entry id=\"4028e4a23e1a7e7c013e28bca5c91332\"></extended-text-entry><pie-rubric id='rubric-4028e4a23e1a7e7c013e28b2be2d1325' />",
"elements": {
"pie-rubric": "@pie-element/rubric@latest",
"extended-text-entry": "@pie-element/extended-text-entry@latest"
"extended-text-entry": "@pie-element/extended-text-entry@latest",
// "pp-pie-element-complex-rubric": "@pie-element/complex-rubric@latest"
}
};

Expand Down
Loading