Merge branch 'main' of https://github.com/microsoft/AzureLTIAssessmentApp into main
This commit is contained in:
Коммит
b193269a5c
|
@ -16,8 +16,10 @@ namespace Assessment.App.Database.Model
|
||||||
|
|
||||||
public List<string> Options { get; set; }
|
public List<string> Options { get; set; }
|
||||||
|
|
||||||
public int Answer { get; set; }
|
public List<string> Answer { get; set; }
|
||||||
|
|
||||||
public string TextType{get;set;}
|
public string TextType{get;set;}
|
||||||
|
|
||||||
|
public string QuestionType{get;set;}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,6 @@
|
||||||
{
|
{
|
||||||
public class QuestionResponseInfo
|
public class QuestionResponseInfo
|
||||||
{
|
{
|
||||||
public int ChosenOption { get; set; }
|
public string[] ChosenOption { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,8 +9,11 @@ namespace Assessment.App.Functions.Student.Dto
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public List<string> Options { get; set; }
|
public List<string> Options { get; set; }
|
||||||
public int ChosenOption { get; set; }
|
public string[] ChosenOption { get; set; }
|
||||||
|
|
||||||
public string TextType{get;set;}
|
public string TextType{get;set;}
|
||||||
|
|
||||||
|
|
||||||
|
public string QuestionType{get;set;}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -118,7 +118,7 @@ namespace Assessment.App.Functions.Student
|
||||||
foreach (var questionId in assessmentItem.QuestionIds)
|
foreach (var questionId in assessmentItem.QuestionIds)
|
||||||
{
|
{
|
||||||
var item = questionItems[questionId];
|
var item = questionItems[questionId];
|
||||||
var chosenOption = -1;
|
string[] chosenOption = new string[]{};
|
||||||
if (studentResponse.Responses.TryGetValue(questionId, out var responseInfo))
|
if (studentResponse.Responses.TryGetValue(questionId, out var responseInfo))
|
||||||
{
|
{
|
||||||
chosenOption = responseInfo.ChosenOption;
|
chosenOption = responseInfo.ChosenOption;
|
||||||
|
@ -131,7 +131,8 @@ namespace Assessment.App.Functions.Student
|
||||||
Description = item.Description,
|
Description = item.Description,
|
||||||
Options = item.Options,
|
Options = item.Options,
|
||||||
ChosenOption = chosenOption,
|
ChosenOption = chosenOption,
|
||||||
TextType=item.TextType
|
TextType=item.TextType,
|
||||||
|
QuestionType = item.QuestionType
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,10 +178,13 @@ namespace Assessment.App.Functions.Student
|
||||||
{
|
{
|
||||||
if (requestData.Responses.TryGetValue(questionId, out var responseInfo))
|
if (requestData.Responses.TryGetValue(questionId, out var responseInfo))
|
||||||
{
|
{
|
||||||
if (responseInfo.ChosenOption == questionItems[questionId].Answer)
|
var questionType = questionItems[questionId].QuestionType;
|
||||||
{
|
var studentAns = responseInfo.ChosenOption;
|
||||||
correctAnswers += 1;
|
studentAns = studentAns.Select(s => s.ToLowerInvariant()).ToArray();
|
||||||
}
|
var correctAns = questionItems[questionId].Answer;
|
||||||
|
correctAns = correctAns.ConvertAll(d => d.ToLower());
|
||||||
|
var res = getResult(studentAns, correctAns.ToArray(), questionType);
|
||||||
|
correctAnswers = correctAnswers + res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,5 +229,27 @@ namespace Assessment.App.Functions.Student
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getResult(string[] studentResponse, string[] correctAns, string questionType){
|
||||||
|
if (questionType == "MCQ" || questionType == "TF"){
|
||||||
|
Array.Sort(studentResponse);
|
||||||
|
Array.Sort(correctAns);
|
||||||
|
if (studentResponse.SequenceEqual(correctAns)){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (questionType == "QA"){
|
||||||
|
foreach(string answer in studentResponse ){
|
||||||
|
if (correctAns.Contains(answer)){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -180,7 +180,8 @@ namespace Assessment.App.Functions.Teacher
|
||||||
Name = questionItem.Name,
|
Name = questionItem.Name,
|
||||||
Options = questionItem.Options,
|
Options = questionItem.Options,
|
||||||
LastModified = DateTime.UtcNow,
|
LastModified = DateTime.UtcNow,
|
||||||
TextType = questionItem.TextType
|
TextType = questionItem.TextType,
|
||||||
|
QuestionType = questionItem.QuestionType,
|
||||||
|
|
||||||
});
|
});
|
||||||
return new OkObjectResult(new CreateQuestionResponse() {Id = id});
|
return new OkObjectResult(new CreateQuestionResponse() {Id = id});
|
||||||
|
@ -213,6 +214,8 @@ namespace Assessment.App.Functions.Teacher
|
||||||
Name = questionItem.Name,
|
Name = questionItem.Name,
|
||||||
Options = questionItem.Options,
|
Options = questionItem.Options,
|
||||||
LastModified = DateTime.UtcNow,
|
LastModified = DateTime.UtcNow,
|
||||||
|
TextType = questionItem.TextType,
|
||||||
|
QuestionType = questionItem.QuestionType
|
||||||
});
|
});
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,8 @@ export const AssessmentStatisticsComponent = (
|
||||||
incorrect: 0,
|
incorrect: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value.chosenOption === question.answer) {
|
var correctAnswers = new Set(question.answer)
|
||||||
|
if (correctAnswers.has(value.chosenOption.toString())) {
|
||||||
data[questionId].correct += 1;
|
data[questionId].correct += 1;
|
||||||
} else {
|
} else {
|
||||||
data[questionId].incorrect += 1;
|
data[questionId].incorrect += 1;
|
||||||
|
|
|
@ -6,31 +6,40 @@ import {
|
||||||
Label,
|
Label,
|
||||||
mergeStyles,
|
mergeStyles,
|
||||||
PrimaryButton,
|
PrimaryButton,
|
||||||
TextField
|
TextField,
|
||||||
|
Dropdown,
|
||||||
|
ICheckboxProps,
|
||||||
|
Checkbox,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import {Question} from "../model/Question";
|
|
||||||
import {Col, Container, Row} from "react-grid-system";
|
import {Col, Container, Row} from "react-grid-system";
|
||||||
|
|
||||||
const optionRootClass = mergeStyles({display: 'flex', alignItems: 'baseline'});
|
const optionRootClass = mergeStyles({display: 'flex', alignItems: 'baseline'});
|
||||||
const textFieldStyles: Partial<ITextFieldStyles> = {fieldGroup: {width: 350}};
|
const textFieldStyles: Partial<ITextFieldStyles> = {fieldGroup: {width: 350}};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface EditQuestionComponentProps {
|
interface EditQuestionComponentProps {
|
||||||
question: Question;
|
question: any;
|
||||||
setQuestion: (f: (oldValue: Question) => Question) => void;
|
setQuestion: (f: (oldValue: any ) => any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const EditQuestionComponent = (
|
export const EditQuestionComponent = (
|
||||||
{question, setQuestion}: EditQuestionComponentProps
|
{question, setQuestion}: EditQuestionComponentProps
|
||||||
) => {
|
) => {
|
||||||
const createOptions = (): IChoiceGroupOption[] => {
|
|
||||||
const createOneOption = (optionId: number): IChoiceGroupOption => {
|
const shouldCheckBoxBeChecked = (index:number):boolean => {
|
||||||
|
if (question.answer.find((x: string) => x === index.toString())){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createOptions = (): ICheckboxProps[] => {
|
||||||
|
const createOneOption = (optionId: number): ICheckboxProps => {
|
||||||
return {
|
return {
|
||||||
key: optionId.toString(),
|
id: optionId.toString(),
|
||||||
text: '',
|
onRenderLabel: (props, render) => {
|
||||||
onRenderField: (props, render) => {
|
|
||||||
return (
|
return (
|
||||||
<div className={optionRootClass}>
|
<div className={optionRootClass}>
|
||||||
{render!(props)}
|
{render!(props)}
|
||||||
|
@ -58,24 +67,129 @@ export const EditQuestionComponent = (
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
onChange: (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean) => {
|
||||||
|
var previousArray:string[] = Object.assign([], question.answer)
|
||||||
|
if (ev?.currentTarget.id !== undefined){
|
||||||
|
if (checked){
|
||||||
|
if (previousArray.includes(ev?.currentTarget.id) === false){
|
||||||
|
previousArray.push(ev?.currentTarget.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
const index = previousArray.indexOf(ev?.currentTarget.id, 0);
|
||||||
|
if (index > -1) {
|
||||||
|
previousArray.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setQuestion({...question, answer:previousArray})
|
||||||
|
},
|
||||||
|
defaultChecked: shouldCheckBoxBeChecked(optionId)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return question.options.map((_: any, index: number) => createOneOption(index));
|
return question.options.map((_: any, index: number) => createOneOption(index));
|
||||||
};
|
};
|
||||||
const updateCorrectAnswer = (_: any, option?: IChoiceGroupOption) => {
|
const updateCorrectAnswer = (_: any, option?: IChoiceGroupOption) => {
|
||||||
var answer: number;
|
var answer: string[];
|
||||||
if (option == null) {
|
if (option == null) {
|
||||||
answer = -1;
|
answer = [];
|
||||||
} else {
|
} else {
|
||||||
answer = Number(option.key);
|
answer = [option.key.toString()];
|
||||||
}
|
}
|
||||||
setQuestion(q => ({...q, answer: answer}))
|
setQuestion(q => ({...q, answer: answer}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mcqDisplay(isOn:boolean){
|
||||||
|
if (isOn){
|
||||||
|
return <> <Row>
|
||||||
|
<Col md={2}>
|
||||||
|
<Label style={{ textAlign: "left" }}>Options</Label>
|
||||||
|
</Col>
|
||||||
|
<Col md={6}>
|
||||||
|
{createOptions().map((value:ICheckboxProps , index: number) => <Checkbox {...value}/>)}
|
||||||
|
</Col>
|
||||||
|
</Row><br /><Row>
|
||||||
|
<Col md={2} />
|
||||||
|
<Col md={6}>
|
||||||
|
<PrimaryButton text="Add option" onClick={() => setQuestion(
|
||||||
|
q => ({ ...q, options: [...q.options, ''] })
|
||||||
|
)} />
|
||||||
|
</Col>
|
||||||
|
</Row></>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function tfDisplay(isOn:Boolean){
|
||||||
|
if (isOn){
|
||||||
|
return <> <Row>
|
||||||
|
<Col md={2}>
|
||||||
|
<Label style={{ textAlign: "left" }}>Options</Label>
|
||||||
|
</Col>
|
||||||
|
<Col md={6}>
|
||||||
|
<ChoiceGroup
|
||||||
|
options={[{key:'0', text:'True'}, {key:'1', text:'False'}]}
|
||||||
|
required={true}
|
||||||
|
selectedKey={`${question.answer}`}
|
||||||
|
onChange={updateCorrectAnswer}/>
|
||||||
|
</Col>
|
||||||
|
</Row></>
|
||||||
|
}
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
function qaDisplay(isOn:boolean){
|
||||||
|
if (isOn){
|
||||||
|
return <>
|
||||||
|
<Row>
|
||||||
|
<Col md={2}>
|
||||||
|
<Label style={{textAlign: "left"}}>Correct answer/s</Label>
|
||||||
|
</Col>
|
||||||
|
<Col md={6}>
|
||||||
|
{question.answer.map((_: any, index: number) => ( <>
|
||||||
|
<div className={optionRootClass}>
|
||||||
|
<TextField
|
||||||
|
id={`question-answer-${index}`}
|
||||||
|
styles={textFieldStyles}
|
||||||
|
value={question.answer[index]}
|
||||||
|
onChange={(_: any, newValue?: string) => setQuestion(q => {
|
||||||
|
var result = { ...q, answer: [...q.answer] };
|
||||||
|
result.answer[index] = newValue || '';
|
||||||
|
return result;
|
||||||
|
})} /><IconButton
|
||||||
|
iconProps={{ iconName: "Delete" }}
|
||||||
|
onClick={() => {
|
||||||
|
setQuestion(q => {
|
||||||
|
let result = { ...q, answer: [...q.answer] };
|
||||||
|
result.answer.splice(index, 1);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
} }
|
||||||
|
disabled={question.answer.length <= 1} />
|
||||||
|
</div>
|
||||||
|
<br></br>
|
||||||
|
</>))}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col md={2} />
|
||||||
|
<Col md={6}>
|
||||||
|
<PrimaryButton text="Add answer" onClick={() => setQuestion(
|
||||||
|
q => ({ ...q, answer: [...q.answer, ''] })
|
||||||
|
)} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
|
|
||||||
|
}
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
return (<Container style={{margin: '30px', position: 'relative'}}>
|
return (<Container style={{margin: '30px', position: 'relative'}}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={2}>
|
<Col md={2}>
|
||||||
<Label style={{textAlign: "left"}}>Name</Label>
|
<Label style={{textAlign: "left"}}>Question Name</Label>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={6}>
|
<Col md={6}>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -90,7 +204,7 @@ export const EditQuestionComponent = (
|
||||||
<br/>
|
<br/>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={2}>
|
<Col md={2}>
|
||||||
<Label style={{textAlign: "left"}}>Description</Label>
|
<Label style={{textAlign: "left"}}>Question Description</Label>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={6}>
|
<Col md={6}>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -108,42 +222,31 @@ export const EditQuestionComponent = (
|
||||||
<br/>
|
<br/>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={2}>
|
<Col md={2}>
|
||||||
<Label style={{textAlign: "left"}}>Text format</Label>
|
<Label style={{textAlign: "left"}}>Question format</Label>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={6}>
|
<Col md = {6}>
|
||||||
<TextField
|
<Dropdown
|
||||||
id="textType-input"
|
defaultSelectedKey={question.textType}
|
||||||
rows={1}
|
options={[{text:"Text", key: 'text'}, {text:"HTML", key:'html'}]}
|
||||||
value={question.textType}
|
onChange={(_, key)=> setQuestion({...question, textType:key?.key})} />
|
||||||
onChange={(_: any, newValue?: string) =>
|
|
||||||
setQuestion(q => ({...q, textType: newValue || ''}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<br/>
|
<br/>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={2}>
|
<Col md={2}>
|
||||||
<Label style={{textAlign: "left"}}>Options</Label>
|
<Label style={{textAlign: "left"}}>Question Type</Label>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={6}>
|
<Col md={6}>
|
||||||
<ChoiceGroup
|
<Dropdown
|
||||||
options={createOptions()}
|
defaultSelectedKey={question.questionType}
|
||||||
required={true}
|
options={[{text:"Multiple Choice", key: 'MCQ'}, {text:"True/False", key:'TF'}, {text:"Question/Answer", key:'QA'}]}
|
||||||
selectedKey={`${question.answer}`}
|
onChange={(_, key)=> setQuestion({...question, questionType: key?.key.toString() || '', answer:[], options:[]})} />
|
||||||
onChange={updateCorrectAnswer}/>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<br/>
|
<br/>
|
||||||
<Row>
|
{mcqDisplay(question.questionType === "MCQ")}
|
||||||
<Col md={2}/>
|
{qaDisplay(question.questionType === "QA")}
|
||||||
<Col md={6}>
|
{tfDisplay(question.questionType === "TF")}
|
||||||
<PrimaryButton text="Add option" onClick={() => setQuestion(
|
|
||||||
q => ({...q, options: [...q.options, '']})
|
|
||||||
)}/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +1,123 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {StudentQuestion} from '../model/StudentQuestion';
|
import {StudentQuestion} from '../model/StudentQuestion';
|
||||||
import {ChoiceGroup, IChoiceGroupOption} from '@fluentui/react/lib/ChoiceGroup';
|
import { IChoiceGroupOption} from '@fluentui/react/lib/ChoiceGroup';
|
||||||
import { Label } from "@fluentui/react";
|
import { Checkbox, ICheckboxProps, Label, TextField } from "@fluentui/react";
|
||||||
import { getTheme } from '@fluentui/react';
|
import { getTheme } from '@fluentui/react';
|
||||||
import parse from 'html-react-parser';
|
import parse from 'html-react-parser';
|
||||||
|
|
||||||
interface StudentQuestionComponentProps {
|
interface StudentQuestionComponentProps {
|
||||||
question: StudentQuestion;
|
question: StudentQuestion;
|
||||||
selectedOption: number;
|
selectedOption: string[];
|
||||||
setSelectedOption: (choice: number) => void;
|
setSelectedOption: (choice: string[]) => void;
|
||||||
}
|
}
|
||||||
var getDisplay = (x:string, texttype:string)=> {
|
var parseDisplay = (x:string, texttype:string)=> {
|
||||||
console.log("text type is");
|
if (texttype === "html" || texttype === "text/html" || texttype === "HTML"){
|
||||||
console.log(texttype);
|
|
||||||
if (texttype === "html" || texttype === "text/html"){
|
|
||||||
x = x.replaceAll('\\n','<br>')
|
x = x.replaceAll('\\n','<br>')
|
||||||
return parse(x)
|
return parse(x)
|
||||||
}
|
}
|
||||||
// Need to handle case where texttype is markdown
|
// Need to handle case where texttype is markdown
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StudentQuestionComponent = (
|
export const StudentQuestionComponent = (
|
||||||
{question, selectedOption, setSelectedOption}: StudentQuestionComponentProps
|
{question, selectedOption, setSelectedOption}: StudentQuestionComponentProps
|
||||||
) => {
|
) => {
|
||||||
const theme = getTheme();
|
const theme = getTheme();
|
||||||
const options: IChoiceGroupOption[] = question.options.map((value, index) => ({
|
|
||||||
key: index.toString(),
|
const shouldCheckBoxBeChecked = (index:number):boolean => {
|
||||||
text: value,
|
if (index in selectedOption){
|
||||||
}));
|
return true;
|
||||||
return (
|
}
|
||||||
<>
|
else{
|
||||||
<br/>
|
return false;
|
||||||
<div style={{
|
}
|
||||||
backgroundColor: '#faf9f8',
|
}
|
||||||
width: '50%',
|
|
||||||
margin: 'auto',
|
const createOptions = (): ICheckboxProps[] => {
|
||||||
padding: '10px',
|
const createOneOption = (optionId: number): ICheckboxProps => {
|
||||||
boxShadow: theme.effects.elevation8
|
return {
|
||||||
}}>
|
id: optionId.toString().concat(question.id) ,
|
||||||
<br/>
|
defaultChecked: shouldCheckBoxBeChecked(optionId),
|
||||||
<div style={{margin: '30px', textAlign: 'left'}}>
|
onChange: (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean) => {
|
||||||
<Label style={{textAlign: 'left', fontSize: '25px'}}>Question</Label>
|
var previousArray:string[] = Object.assign([], selectedOption)
|
||||||
<p>{getDisplay(question.description, question.textType)}</p>
|
if (ev?.currentTarget.id !== undefined){
|
||||||
{console.log(question)}
|
if (checked){
|
||||||
<ChoiceGroup
|
if (previousArray.includes(optionId.toString()) === false){
|
||||||
selectedKey={selectedOption.toString()}
|
previousArray.push(optionId.toString())
|
||||||
onChange={(_: any, option) => {
|
}
|
||||||
if (option) {
|
}
|
||||||
setSelectedOption(Number(option.key));
|
else{
|
||||||
|
const index = previousArray.indexOf(optionId.toString(), 0);
|
||||||
|
if (index > -1) {
|
||||||
|
previousArray.splice(index, 1);
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
options={options}
|
}
|
||||||
/>
|
setSelectedOption(previousArray)
|
||||||
|
},
|
||||||
|
label:question.options[optionId]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return question.options.map((_: any, index: number) => createOneOption(index));
|
||||||
|
};
|
||||||
|
|
||||||
|
function displayMCQOrTF(){
|
||||||
|
return <div style={{margin: '30px', textAlign: 'left'}}>
|
||||||
|
<Label style={{textAlign: 'left', fontSize: '25px'}}>Question</Label>
|
||||||
|
<p>
|
||||||
|
{parseDisplay(question.description, question.textType)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{
|
||||||
|
createOptions().map((value:ICheckboxProps , index: number) =>
|
||||||
|
<>
|
||||||
|
<Checkbox {...value}/>
|
||||||
|
<br/>
|
||||||
|
</>)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayQA(){
|
||||||
|
return <div style={{margin: '30px', textAlign: 'left'}}>
|
||||||
|
<Label style={{textAlign: 'left', fontSize: '25px'}}>Question</Label>
|
||||||
|
<p>{parseDisplay(question.description, question.textType)}</p>
|
||||||
|
<TextField
|
||||||
|
id="LA-input"
|
||||||
|
onChange={(_: any, newValue?: string) =>
|
||||||
|
setSelectedOption([newValue|| ''])}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (question.questionType == "QA") {
|
||||||
|
return <>
|
||||||
|
<br />
|
||||||
|
<div style={{backgroundColor: '#faf9f8', width: '50%',margin: 'auto', padding: '10px', boxShadow: theme.effects.elevation8}}>
|
||||||
|
<br />
|
||||||
|
{displayQA()}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<br/>
|
||||||
|
<div style={{
|
||||||
|
backgroundColor: '#faf9f8',
|
||||||
|
width: '50%',
|
||||||
|
margin: 'auto',
|
||||||
|
padding: '10px',
|
||||||
|
boxShadow: theme.effects.elevation8
|
||||||
|
}}>
|
||||||
|
<br/>
|
||||||
|
{displayMCQOrTF()}
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
</>
|
||||||
<br/>
|
)
|
||||||
</div>
|
}
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,8 +187,9 @@ export class FakeRepository implements IRepository {
|
||||||
description: "Applications of machine learning are all around us",
|
description: "Applications of machine learning are all around us",
|
||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
options: ["True", "False"],
|
options: ["True", "False"],
|
||||||
answer: 0,
|
answer: ['0'],
|
||||||
textType:"text",
|
textType:"text",
|
||||||
|
questionType:"TF"
|
||||||
},
|
},
|
||||||
'1': {
|
'1': {
|
||||||
id: '1',
|
id: '1',
|
||||||
|
@ -200,8 +201,9 @@ export class FakeRepository implements IRepository {
|
||||||
"The use of neural networks",
|
"The use of neural networks",
|
||||||
"Deep learning is used in robots",
|
"Deep learning is used in robots",
|
||||||
],
|
],
|
||||||
answer: 1,
|
answer: ['1'],
|
||||||
textType:"text"
|
textType:"text",
|
||||||
|
questionType:"MCQ",
|
||||||
},
|
},
|
||||||
'2': {
|
'2': {
|
||||||
id: '2',
|
id: '2',
|
||||||
|
@ -213,8 +215,9 @@ export class FakeRepository implements IRepository {
|
||||||
"To customise a shopping experience based on the type of the customer",
|
"To customise a shopping experience based on the type of the customer",
|
||||||
"Both of the above",
|
"Both of the above",
|
||||||
],
|
],
|
||||||
answer: 2,
|
answer: ['2'],
|
||||||
textType:"text"
|
textType:"text",
|
||||||
|
questionType:"MCQ"
|
||||||
},
|
},
|
||||||
'3': {
|
'3': {
|
||||||
id: '3',
|
id: '3',
|
||||||
|
@ -227,8 +230,9 @@ export class FakeRepository implements IRepository {
|
||||||
"Documentations",
|
"Documentations",
|
||||||
"All of the above",
|
"All of the above",
|
||||||
],
|
],
|
||||||
answer: 3,
|
answer: ['3'],
|
||||||
textType:"text"
|
textType:"text",
|
||||||
|
questionType:"MCQ",
|
||||||
},
|
},
|
||||||
'4': {
|
'4': {
|
||||||
id: '4',
|
id: '4',
|
||||||
|
@ -241,8 +245,9 @@ export class FakeRepository implements IRepository {
|
||||||
"System Models",
|
"System Models",
|
||||||
"Software Models",
|
"Software Models",
|
||||||
],
|
],
|
||||||
answer: 1,
|
answer: ['1'],
|
||||||
textType:"text"
|
textType:"text",
|
||||||
|
questionType:"MCQ"
|
||||||
},
|
},
|
||||||
'5': {
|
'5': {
|
||||||
id: '5',
|
id: '5',
|
||||||
|
@ -255,8 +260,9 @@ export class FakeRepository implements IRepository {
|
||||||
"3",
|
"3",
|
||||||
"5",
|
"5",
|
||||||
],
|
],
|
||||||
answer: 2,
|
answer: ['2'],
|
||||||
textType:"text"
|
textType:"text",
|
||||||
|
questionType:"MCQ"
|
||||||
},
|
},
|
||||||
'6': {
|
'6': {
|
||||||
id: '6',
|
id: '6',
|
||||||
|
@ -269,8 +275,9 @@ export class FakeRepository implements IRepository {
|
||||||
"Increasing complexity",
|
"Increasing complexity",
|
||||||
"Self-regulation",
|
"Self-regulation",
|
||||||
],
|
],
|
||||||
answer: 0,
|
answer:['0'],
|
||||||
textType:"text"
|
textType:"text",
|
||||||
|
questionType:"MCQ"
|
||||||
},
|
},
|
||||||
'7': {
|
'7': {
|
||||||
id: '7',
|
id: '7',
|
||||||
|
@ -283,8 +290,9 @@ export class FakeRepository implements IRepository {
|
||||||
"Maintenance",
|
"Maintenance",
|
||||||
"All of the above",
|
"All of the above",
|
||||||
],
|
],
|
||||||
answer: 3,
|
answer: ['3'],
|
||||||
textType:"text"
|
textType:"text",
|
||||||
|
questionType:"MCQ"
|
||||||
},
|
},
|
||||||
'8': {
|
'8': {
|
||||||
id: '8',
|
id: '8',
|
||||||
|
@ -297,8 +305,9 @@ export class FakeRepository implements IRepository {
|
||||||
"Software Quality Management",
|
"Software Quality Management",
|
||||||
"All of the above",
|
"All of the above",
|
||||||
],
|
],
|
||||||
answer: 3,
|
answer: ['3'],
|
||||||
textType:"text"
|
textType:"text",
|
||||||
|
questionType:"MCQ"
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -363,7 +372,7 @@ export class FakeRepository implements IRepository {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async submitStudentAssessment(assessmentId: string, chosenOptions: { [id: string]: number }) {}
|
public async submitStudentAssessment(assessmentId: string, chosenOptions: { [id: string]: string[] }) {}
|
||||||
|
|
||||||
public isReady(): boolean {
|
public isReady(): boolean {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -22,7 +22,7 @@ export interface IRepository {
|
||||||
getAssessmentStats(assessmentId: string): Promise<AssessmentStatistics>;
|
getAssessmentStats(assessmentId: string): Promise<AssessmentStatistics>;
|
||||||
getStudentAssessment(assessmentId: string): Promise<StudentAssessment>;
|
getStudentAssessment(assessmentId: string): Promise<StudentAssessment>;
|
||||||
getStudentQuestions(assessmentId: string): Promise<StudentAssessmentQuestions>;
|
getStudentQuestions(assessmentId: string): Promise<StudentAssessmentQuestions>;
|
||||||
submitStudentAssessment(assessmentId: string, chosenOptions: { [id: string]: number }): void;
|
submitStudentAssessment(assessmentId: string, chosenOptions: { [id: string]: string[] }): void;
|
||||||
deleteQuestions(questionIds: string[]): void;
|
deleteQuestions(questionIds: string[]): void;
|
||||||
deleteQuestionBanks(questionBankIds: string[]): void;
|
deleteQuestionBanks(questionBankIds: string[]): void;
|
||||||
isReady(): boolean;
|
isReady(): boolean;
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
export interface Question {
|
export abstract class Question {
|
||||||
id: string,
|
id: string;
|
||||||
name: string,
|
name: string;
|
||||||
description: string,
|
description: string;
|
||||||
lastModified: Date,
|
lastModified: Date;
|
||||||
options: string[],
|
answer: string[];
|
||||||
answer: number,
|
textType:string;
|
||||||
textType:string,
|
questionType:string;
|
||||||
|
options:string[]
|
||||||
|
|
||||||
|
constructor(id:string, name:string, description:string, lastModified:Date
|
||||||
|
,answer:any, textType:string, questionType:string){
|
||||||
|
this.id= id;
|
||||||
|
this.name=name;
|
||||||
|
this.description=description;
|
||||||
|
this.lastModified=lastModified;
|
||||||
|
this.textType="text";
|
||||||
|
this.answer=[''];
|
||||||
|
this.questionType=questionType
|
||||||
|
this.options=['', '']
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export interface QuestionResponseInfo {
|
export interface QuestionResponseInfo {
|
||||||
chosenOption: number,
|
chosenOption: string[],
|
||||||
}
|
}
|
|
@ -220,7 +220,7 @@ export class Repository implements IRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async submitStudentAssessment(assessmentId: string, chosenOptions: { [id: string]: number }) {
|
public async submitStudentAssessment(assessmentId: string, chosenOptions: { [id: string]: string[] }) {
|
||||||
const accessToken = await this.getAccessToken();
|
const accessToken = await this.getAccessToken();
|
||||||
const request: SubmitStudentAssessmentRequest = {
|
const request: SubmitStudentAssessmentRequest = {
|
||||||
responses: Object.entries(chosenOptions).reduce((a, item) => ({
|
responses: Object.entries(chosenOptions).reduce((a, item) => ({
|
||||||
|
|
|
@ -3,6 +3,7 @@ export interface StudentQuestion {
|
||||||
name: string,
|
name: string,
|
||||||
description: string,
|
description: string,
|
||||||
options: string[],
|
options: string[],
|
||||||
chosenOption: number,
|
chosenOption: string[],
|
||||||
textType:string,
|
textType:string,
|
||||||
|
questionType:string
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@ import { Question } from "../Question";
|
||||||
export class GiftParser extends AssessmentAppParser{
|
export class GiftParser extends AssessmentAppParser{
|
||||||
|
|
||||||
public parse(): void {
|
public parse(): void {
|
||||||
|
|
||||||
|
|
||||||
var questionBankTitle:string = "Not defined yet";
|
var questionBankTitle:string = "Not defined yet";
|
||||||
var questions:Question[] = [];
|
var questions:Question[] = [];
|
||||||
|
|
||||||
|
@ -24,12 +22,13 @@ export class GiftParser extends AssessmentAppParser{
|
||||||
|
|
||||||
var choices:TextChoice[] = q.choices;
|
var choices:TextChoice[] = q.choices;
|
||||||
var answerTexts = Array();
|
var answerTexts = Array();
|
||||||
var correctAnswer = 0;
|
var correctAnswer = [];
|
||||||
for (var choice in choices){
|
for (var choice in choices){
|
||||||
var details:TextChoice = choices[choice];
|
var details:TextChoice = choices[choice];
|
||||||
answerTexts.push(this.removeTags(details.text['text']));
|
answerTexts.push(this.removeTags(details.text['text']));
|
||||||
if (details.isCorrect){
|
var weight = details.weight
|
||||||
correctAnswer = +choice; // plus operator converts to number
|
if (weight != null && weight > 0){
|
||||||
|
correctAnswer.push(choice); // plus operator converts to number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var stem:TextFormat = q.stem;
|
var stem:TextFormat = q.stem;
|
||||||
|
@ -40,7 +39,8 @@ export class GiftParser extends AssessmentAppParser{
|
||||||
lastModified: new Date (),
|
lastModified: new Date (),
|
||||||
options: answerTexts,
|
options: answerTexts,
|
||||||
answer: correctAnswer,
|
answer: correctAnswer,
|
||||||
textType:stem.format
|
textType:stem.format,
|
||||||
|
questionType: "MCQ",
|
||||||
}
|
}
|
||||||
questions.push(question);
|
questions.push(question);
|
||||||
|
|
||||||
|
@ -55,13 +55,27 @@ export class GiftParser extends AssessmentAppParser{
|
||||||
description: stem.text,
|
description: stem.text,
|
||||||
lastModified: new Date (),
|
lastModified: new Date (),
|
||||||
options: ["True", "False"],
|
options: ["True", "False"],
|
||||||
answer: ans? 0:1,
|
answer: ans? ['0']:['1'],
|
||||||
textType:stem.format
|
textType:stem.format,
|
||||||
|
questionType: "TF",
|
||||||
}
|
}
|
||||||
questions.push(question);
|
questions.push(question);
|
||||||
}
|
}
|
||||||
|
if (q.type === "Description"|| q.type === "Essay" || q.type === "Short"){
|
||||||
|
var stem:TextFormat = q.stem;
|
||||||
|
const question: Question = {
|
||||||
|
id: "",
|
||||||
|
name: this.removeTags(stem.text),
|
||||||
|
description: stem.text,
|
||||||
|
lastModified: new Date (),
|
||||||
|
options: ["", ""],
|
||||||
|
answer: ["This is an example"], // Gift format does not give "ideal" anwer for these types
|
||||||
|
textType:stem.format,
|
||||||
|
questionType:"QA",
|
||||||
|
}
|
||||||
|
questions.push(question);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var qb: ParsedQuestionBank = {
|
var qb: ParsedQuestionBank = {
|
||||||
|
|
|
@ -15,12 +15,12 @@ export class MicrosoftOSCParser extends AssessmentAppParser{
|
||||||
var questions:Question[] = [];
|
var questions:Question[] = [];
|
||||||
for (let question of rawQuestion.quiz){
|
for (let question of rawQuestion.quiz){
|
||||||
var answerTexts = Array();
|
var answerTexts = Array();
|
||||||
var correctAnswer = 0;
|
var correctAnswer = [];
|
||||||
var counter = 0;
|
var counter = 0;
|
||||||
for (let option of question.answerOptions){
|
for (let option of question.answerOptions){
|
||||||
answerTexts.push(option.answerText)
|
answerTexts.push(option.answerText)
|
||||||
if (option.isCorrect == "true"){
|
if (option.isCorrect == "true"){
|
||||||
correctAnswer = counter ;
|
correctAnswer.push(counter.toString());
|
||||||
}
|
}
|
||||||
counter = counter + 1;
|
counter = counter + 1;
|
||||||
|
|
||||||
|
@ -28,12 +28,13 @@ export class MicrosoftOSCParser extends AssessmentAppParser{
|
||||||
|
|
||||||
const questionToSave: Question = {
|
const questionToSave: Question = {
|
||||||
id: "",
|
id: "",
|
||||||
name: question.questionText,
|
name: question.questionText,
|
||||||
description: question.questionText,
|
description: question.questionText,
|
||||||
lastModified: new Date (),
|
lastModified: new Date(),
|
||||||
options: answerTexts,
|
options: answerTexts,
|
||||||
answer: correctAnswer,
|
answer: correctAnswer,
|
||||||
textType:"text"
|
textType: "text",
|
||||||
|
questionType: "MCQ"
|
||||||
}
|
}
|
||||||
questions.push(questionToSave);
|
questions.push(questionToSave);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { ParsedQuestionBank } from "./ParsedQuestionBank";
|
||||||
import { AssessmentAppParser } from "./Parser";
|
import { AssessmentAppParser } from "./Parser";
|
||||||
import { Question } from "../Question";
|
import { Question } from "../Question";
|
||||||
|
|
||||||
|
// Currently only supports MCQs and TFs
|
||||||
export class OriginalAppParser extends AssessmentAppParser{
|
export class OriginalAppParser extends AssessmentAppParser{
|
||||||
public parse() {
|
public parse() {
|
||||||
const rawData = JSON.parse(this.raw);
|
const rawData = JSON.parse(this.raw);
|
||||||
|
@ -13,10 +14,11 @@ export class OriginalAppParser extends AssessmentAppParser{
|
||||||
id: "",
|
id: "",
|
||||||
name: rawQuestion.name,
|
name: rawQuestion.name,
|
||||||
description: rawQuestion.description,
|
description: rawQuestion.description,
|
||||||
lastModified: new Date (),
|
lastModified: new Date(),
|
||||||
options: rawQuestion.options,
|
options: rawQuestion.options,
|
||||||
answer: rawQuestion.answer,
|
answer: rawQuestion.answer,
|
||||||
textType: rawQuestion.textType
|
textType: rawQuestion.textType,
|
||||||
|
questionType: rawQuestion.questionType
|
||||||
}
|
}
|
||||||
questions.push(question);
|
questions.push(question);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ export class AssessmentAppParser {
|
||||||
str = str.toString();
|
str = str.toString();
|
||||||
str = str.replace( /(<([^>]+)>)/ig, '')
|
str = str.replace( /(<([^>]+)>)/ig, '')
|
||||||
str = str.replaceAll('\\n','')
|
str = str.replaceAll('\\n','')
|
||||||
|
str = str.replace(/\ /g, "")
|
||||||
return str;}
|
return str;}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,16 @@ import { ParsedQuestionBank } from "./ParsedQuestionBank";
|
||||||
import { AssessmentAppParser } from "./Parser";
|
import { AssessmentAppParser } from "./Parser";
|
||||||
import { Question } from "../Question";
|
import { Question } from "../Question";
|
||||||
|
|
||||||
|
|
||||||
|
interface questionTypeSpecificParse {
|
||||||
|
correctAnswer:string[]
|
||||||
|
options:string[]
|
||||||
|
};
|
||||||
export class QTIParser extends AssessmentAppParser{
|
export class QTIParser extends AssessmentAppParser{
|
||||||
options = {
|
options = {
|
||||||
ignoreAttributes:false
|
ignoreAttributes:false
|
||||||
};
|
};
|
||||||
|
|
||||||
public parse() {
|
public parse() {
|
||||||
var questionBankTitle:string = "Not defined yet";
|
var questionBankTitle:string = "Not defined yet";
|
||||||
var questions:Question[] = [];
|
var questions:Question[] = [];
|
||||||
|
@ -19,40 +25,57 @@ export class QTIParser extends AssessmentAppParser{
|
||||||
|
|
||||||
for (let questionId in questionsSection){
|
for (let questionId in questionsSection){
|
||||||
var currQuestion = questionsSection[questionId];
|
var currQuestion = questionsSection[questionId];
|
||||||
|
// Get question title
|
||||||
var questionTitle = currQuestion['@_title'];
|
var questionTitle = currQuestion['@_title'];
|
||||||
|
|
||||||
|
// Get question type
|
||||||
var qMetaDataField = currQuestion['itemmetadata']['qtimetadata']['qtimetadatafield'];
|
var qMetaDataField = currQuestion['itemmetadata']['qtimetadata']['qtimetadatafield'];
|
||||||
var metaData = qMetaDataField[0]; // 0 position contains question type
|
var metaData = qMetaDataField[0]; // 0 position contains question type
|
||||||
if (metaData['fieldentry'] != 'multiple_choice_question'){
|
var questionType = 'NA';
|
||||||
continue; // As we currently only support MCQs
|
|
||||||
}
|
// Get question description
|
||||||
var questionText = currQuestion['presentation']['material']['mattext']['#text'];
|
var questionText = currQuestion['presentation']['material']['mattext']['#text'];
|
||||||
questionText = questionText.split('\n')[1];
|
|
||||||
|
|
||||||
// Get all options
|
// Get text type
|
||||||
var responseLabels = currQuestion['presentation']['response_lid']['render_choice']['response_label'];
|
var cleanedTextType:string = 'text'
|
||||||
var answerTexts = Array();
|
if (currQuestion['presentation']['material']['mattext']['@_texttype'] === "text/html"){
|
||||||
var correctAnswer = currQuestion['resprocessing']['respcondition']['conditionvar']['varequal']['#text']
|
cleanedTextType = "html";
|
||||||
for (let responseId in responseLabels){
|
|
||||||
var response = responseLabels[responseId];
|
|
||||||
if (correctAnswer == response['@_ident']){
|
|
||||||
correctAnswer = responseId;
|
|
||||||
}
|
|
||||||
answerTexts.push(this.removeTags(response['material']['mattext']['#text']))
|
|
||||||
}
|
}
|
||||||
// Finally creating the question
|
// Get answer and options
|
||||||
|
|
||||||
|
var result:questionTypeSpecificParse = {options:[], correctAnswer:[]}
|
||||||
|
if (metaData['fieldentry'] === 'multiple_choice_question' ){
|
||||||
|
result = this.parseMCQ(currQuestion);
|
||||||
|
questionType = "MCQ";
|
||||||
|
}
|
||||||
|
else if (metaData['fieldentry'] === 'true_false_question'){
|
||||||
|
result = this.parseMCQ(currQuestion);
|
||||||
|
questionType = "TF";
|
||||||
|
}
|
||||||
|
else if (metaData['fieldentry'] === 'multiple_answers_question'){
|
||||||
|
result= this.parseMAQ(currQuestion);
|
||||||
|
questionType = "MCQ";
|
||||||
|
}
|
||||||
|
else if (metaData['fieldentry'] === 'numerical_question'){
|
||||||
|
result= this.parseQA(currQuestion);
|
||||||
|
questionType = "QA"
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const question:Question = {
|
const question:Question = {
|
||||||
id: "",
|
id: "",
|
||||||
name: questionTitle,
|
name: questionTitle,
|
||||||
description: questionText,
|
description: questionText,
|
||||||
lastModified: new Date (),
|
lastModified: new Date(),
|
||||||
options: answerTexts,
|
options: result.options,
|
||||||
answer: correctAnswer,
|
answer: result.correctAnswer,
|
||||||
textType:currQuestion['presentation']['material']['mattext']['@_texttype'],
|
textType: cleanedTextType,
|
||||||
}
|
questionType: questionType
|
||||||
|
}
|
||||||
questions.push(question);
|
questions.push(question);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var qb: ParsedQuestionBank = {
|
var qb: ParsedQuestionBank = {
|
||||||
questionBankTitle: questionBankTitle,
|
questionBankTitle: questionBankTitle,
|
||||||
questions:questions
|
questions:questions
|
||||||
|
@ -61,6 +84,63 @@ export class QTIParser extends AssessmentAppParser{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private parseMCQ(currQuestion:any){
|
||||||
|
// Get all options
|
||||||
|
var responseLabels = currQuestion['presentation']['response_lid']['render_choice']['response_label'];
|
||||||
|
var answerTexts = Array();
|
||||||
|
var correctAnswerId = currQuestion['resprocessing']['respcondition']['conditionvar']['varequal']['#text'].toString();
|
||||||
|
var correctAnswer = ['-1'];
|
||||||
|
for (let responseId in responseLabels){
|
||||||
|
var response = responseLabels[responseId];
|
||||||
|
if (correctAnswerId === response['@_ident'].toString()){
|
||||||
|
correctAnswer = [responseId.toString()];
|
||||||
|
}
|
||||||
|
answerTexts.push(this.removeTags(response['material']['mattext']['#text']))
|
||||||
|
}
|
||||||
|
var result: questionTypeSpecificParse = {options:answerTexts, correctAnswer:correctAnswer}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseMAQ(currQuestion:any){
|
||||||
|
|
||||||
|
// Get all options
|
||||||
|
var responseLabels = currQuestion['presentation']['response_lid']['render_choice']['response_label'];
|
||||||
|
var answerTexts = Array();
|
||||||
|
var correctAnswerQTIIds = currQuestion['resprocessing']['respcondition']['conditionvar']['and']['varequal']
|
||||||
|
var correctAnsIds = new Set();
|
||||||
|
|
||||||
|
for (let id in correctAnswerQTIIds){
|
||||||
|
var answerId = correctAnswerQTIIds[id]['#text'];
|
||||||
|
correctAnsIds.add(answerId.toString());
|
||||||
|
}
|
||||||
|
var correctAns:string[] = Array();
|
||||||
|
for (let responseId in responseLabels){
|
||||||
|
var response = responseLabels[responseId];
|
||||||
|
if (correctAnsIds.has(response['@_ident'].toString())){
|
||||||
|
var correctAnswer = +responseId
|
||||||
|
correctAns.push(correctAnswer.toString())
|
||||||
|
}
|
||||||
|
answerTexts.push(this.removeTags(response['material']['mattext']['#text']))
|
||||||
|
}
|
||||||
|
|
||||||
|
var result: questionTypeSpecificParse = {options:answerTexts, correctAnswer:correctAns}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
private parseQA(currQuestion:any){
|
||||||
|
|
||||||
|
//Get all allowed answers
|
||||||
|
var correctAnswerQTIIds = currQuestion['resprocessing']['respcondition']
|
||||||
|
var correctAns = new Array();
|
||||||
|
for (let index in correctAnswerQTIIds){
|
||||||
|
var curr = correctAnswerQTIIds[index]['conditionvar']['or']['varequal']['#text'];
|
||||||
|
correctAns.push(curr.toString());
|
||||||
|
}
|
||||||
|
var result: questionTypeSpecificParse = {options:[], correctAnswer:correctAns}
|
||||||
|
return result
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {PrimaryButton, Spinner} from '@fluentui/react';
|
import {PrimaryButton, Spinner} from '@fluentui/react';
|
||||||
import * as React from 'react';
|
|
||||||
import {useContext, useEffect, useState} from 'react';
|
import {useContext, useEffect, useState} from 'react';
|
||||||
import {StudentQuestionComponent} from '../../components/StudentQuestionComponent';
|
import {StudentQuestionComponent} from '../../components/StudentQuestionComponent';
|
||||||
import {StudentQuestion} from '../../model/StudentQuestion';
|
import {StudentQuestion} from '../../model/StudentQuestion';
|
||||||
|
@ -17,7 +16,7 @@ export const StudentQuiz = () => {
|
||||||
const {id} = useParams<StudentQuizParams>();
|
const {id} = useParams<StudentQuizParams>();
|
||||||
const repositoryContext = useContext(RepositoryContext);
|
const repositoryContext = useContext(RepositoryContext);
|
||||||
const [questions, setQuestions] = useState<StudentQuestion[]>([])
|
const [questions, setQuestions] = useState<StudentQuestion[]>([])
|
||||||
const [chosenOptions, setChosenOptions] = useState<{ [id: string]: number }>({});
|
const [chosenOptions, setChosenOptions] = useState<{ [id: string]: any }>({});
|
||||||
const [isLoaded, setIsLoaded] = useState(false);
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
const [studentAssessment, setStudentAssessment] = useState<StudentAssessment | null>(null);
|
const [studentAssessment, setStudentAssessment] = useState<StudentAssessment | null>(null);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
|
@ -20,8 +20,9 @@ export const NewQuestionPage = () => {
|
||||||
description: "",
|
description: "",
|
||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
options: ['', ''],
|
options: ['', ''],
|
||||||
answer: -1,
|
answer: [],
|
||||||
textType:""
|
textType:"",
|
||||||
|
questionType:"MCQ",
|
||||||
});
|
});
|
||||||
const {bankId} = useParams<NewQuestionPageParams>();
|
const {bankId} = useParams<NewQuestionPageParams>();
|
||||||
const repositoryContext = React.useContext(RepositoryContext);
|
const repositoryContext = React.useContext(RepositoryContext);
|
||||||
|
@ -46,7 +47,7 @@ export const NewQuestionPage = () => {
|
||||||
padding: '10px',
|
padding: '10px',
|
||||||
boxShadow: theme.effects.elevation8
|
boxShadow: theme.effects.elevation8
|
||||||
}}>
|
}}>
|
||||||
<EditQuestionComponent question={question} setQuestion={setQuestion}/>
|
<EditQuestionComponent question={question} setQuestion={setQuestion}/>
|
||||||
<Container style={{margin: '30px', position: 'relative'}}>
|
<Container style={{margin: '30px', position: 'relative'}}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={2}/>
|
<Col md={2}/>
|
||||||
|
|
|
@ -21,18 +21,20 @@ export const QuestionPage = () => {
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
options: ['', ''],
|
questionType: "",
|
||||||
answer: -1,
|
answer: ['-1'],
|
||||||
textType:"text"
|
textType:"text",
|
||||||
|
options:['', '']
|
||||||
});
|
});
|
||||||
const [savedQuestion, setSavedQuestion] = useState<Question>({
|
const [savedQuestion, setSavedQuestion] = useState<Question>({
|
||||||
id: "",
|
id: "",
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
options: ['', ''],
|
questionType:"",
|
||||||
answer: -1,
|
answer: [],
|
||||||
textType:"text"
|
textType:"text",
|
||||||
|
options:['', '']
|
||||||
})
|
})
|
||||||
const {id} = useParams<QuestionPageParams>();
|
const {id} = useParams<QuestionPageParams>();
|
||||||
const repositoryContext = React.useContext(RepositoryContext);
|
const repositoryContext = React.useContext(RepositoryContext);
|
||||||
|
|
|
@ -105,7 +105,7 @@ export const QuestionBankPage = () => {
|
||||||
if (repositoryContext == null) {
|
if (repositoryContext == null) {
|
||||||
return <p>Question Bank cannot be found</p>
|
return <p>Question Bank cannot be found</p>
|
||||||
}
|
}
|
||||||
const redirectToNewQuestion = () => {
|
const redirectToNewQuestion = () => {
|
||||||
history.push(`/spa/new-question/bank=${id}`);
|
history.push(`/spa/new-question/bank=${id}`);
|
||||||
}
|
}
|
||||||
const _items: ICommandBarItemProps[] = [
|
const _items: ICommandBarItemProps[] = [
|
||||||
|
|
Загрузка…
Ссылка в новой задаче