Merge pull request #27 from neharanadee/main

New Functionalities
This commit is contained in:
Lee Stott 2022-03-10 15:28:46 +00:00 коммит произвёл GitHub
Родитель 658d099bac 788bbf561b
Коммит 05d082df2b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
23 изменённых файлов: 502 добавлений и 179 удалений

Просмотреть файл

@ -16,8 +16,10 @@ namespace Assessment.App.Database.Model
public List<string> Options { get; set; }
public int Answer { get; set; }
public List<string> Answer { get; set; }
public string TextType{get;set;}
public string QuestionType{get;set;}
}
}

Просмотреть файл

@ -2,6 +2,6 @@
{
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 Description { 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 QuestionType{get;set;}
}
}

Просмотреть файл

@ -118,7 +118,7 @@ namespace Assessment.App.Functions.Student
foreach (var questionId in assessmentItem.QuestionIds)
{
var item = questionItems[questionId];
var chosenOption = -1;
string[] chosenOption = new string[]{};
if (studentResponse.Responses.TryGetValue(questionId, out var responseInfo))
{
chosenOption = responseInfo.ChosenOption;
@ -131,7 +131,8 @@ namespace Assessment.App.Functions.Student
Description = item.Description,
Options = item.Options,
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 (responseInfo.ChosenOption == questionItems[questionId].Answer)
{
correctAnswers += 1;
}
var questionType = questionItems[questionId].QuestionType;
var studentAns = responseInfo.ChosenOption;
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;
}
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,
Options = questionItem.Options,
LastModified = DateTime.UtcNow,
TextType = questionItem.TextType
TextType = questionItem.TextType,
QuestionType = questionItem.QuestionType,
});
return new OkObjectResult(new CreateQuestionResponse() {Id = id});
@ -213,6 +214,8 @@ namespace Assessment.App.Functions.Teacher
Name = questionItem.Name,
Options = questionItem.Options,
LastModified = DateTime.UtcNow,
TextType = questionItem.TextType,
QuestionType = questionItem.QuestionType
});
return new OkResult();
}

Просмотреть файл

@ -140,7 +140,8 @@ export const AssessmentStatisticsComponent = (
incorrect: 0,
}
}
if (value.chosenOption === question.answer) {
var correctAnswers = new Set(question.answer)
if (correctAnswers.has(value.chosenOption.toString())) {
data[questionId].correct += 1;
} else {
data[questionId].incorrect += 1;

Просмотреть файл

@ -6,31 +6,40 @@ import {
Label,
mergeStyles,
PrimaryButton,
TextField
TextField,
Dropdown,
ICheckboxProps,
Checkbox,
} from "@fluentui/react";
import {Question} from "../model/Question";
import {Col, Container, Row} from "react-grid-system";
const optionRootClass = mergeStyles({display: 'flex', alignItems: 'baseline'});
const textFieldStyles: Partial<ITextFieldStyles> = {fieldGroup: {width: 350}};
interface EditQuestionComponentProps {
question: Question;
setQuestion: (f: (oldValue: Question) => Question) => void;
question: any;
setQuestion: (f: (oldValue: any ) => any) => void;
}
export const EditQuestionComponent = (
{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 {
key: optionId.toString(),
text: '',
onRenderField: (props, render) => {
id: optionId.toString(),
onRenderLabel: (props, render) => {
return (
<div className={optionRootClass}>
{render!(props)}
@ -58,24 +67,129 @@ export const EditQuestionComponent = (
/>
</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));
};
const updateCorrectAnswer = (_: any, option?: IChoiceGroupOption) => {
var answer: number;
var answer: string[];
if (option == null) {
answer = -1;
answer = [];
} else {
answer = Number(option.key);
answer = [option.key.toString()];
}
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'}}>
<Row>
<Col md={2}>
<Label style={{textAlign: "left"}}>Name</Label>
<Label style={{textAlign: "left"}}>Question Name</Label>
</Col>
<Col md={6}>
<TextField
@ -90,7 +204,7 @@ export const EditQuestionComponent = (
<br/>
<Row>
<Col md={2}>
<Label style={{textAlign: "left"}}>Description</Label>
<Label style={{textAlign: "left"}}>Question Description</Label>
</Col>
<Col md={6}>
<TextField
@ -108,42 +222,31 @@ export const EditQuestionComponent = (
<br/>
<Row>
<Col md={2}>
<Label style={{textAlign: "left"}}>Text format</Label>
<Label style={{textAlign: "left"}}>Question format</Label>
</Col>
<Col md={6}>
<TextField
id="textType-input"
rows={1}
value={question.textType}
onChange={(_: any, newValue?: string) =>
setQuestion(q => ({...q, textType: newValue || ''}))
}
/>
<Col md = {6}>
<Dropdown
defaultSelectedKey={question.textType}
options={[{text:"Text", key: 'text'}, {text:"HTML", key:'html'}]}
onChange={(_, key)=> setQuestion({...question, textType:key?.key})} />
</Col>
</Row>
<br/>
<Row>
<Col md={2}>
<Label style={{textAlign: "left"}}>Options</Label>
<Label style={{textAlign: "left"}}>Question Type</Label>
</Col>
<Col md={6}>
<ChoiceGroup
options={createOptions()}
required={true}
selectedKey={`${question.answer}`}
onChange={updateCorrectAnswer}/>
<Dropdown
defaultSelectedKey={question.questionType}
options={[{text:"Multiple Choice", key: 'MCQ'}, {text:"True/False", key:'TF'}, {text:"Question/Answer", key:'QA'}]}
onChange={(_, key)=> setQuestion({...question, questionType: key?.key.toString() || '', answer:[], options:[]})} />
</Col>
</Row>
<br/>
<Row>
<Col md={2}/>
<Col md={6}>
<PrimaryButton text="Add option" onClick={() => setQuestion(
q => ({...q, options: [...q.options, '']})
)}/>
</Col>
</Row>
{mcqDisplay(question.questionType === "MCQ")}
{qaDisplay(question.questionType === "QA")}
{tfDisplay(question.questionType === "TF")}
</Container>
);
}

Просмотреть файл

@ -1,63 +1,123 @@
import React from "react";
import {StudentQuestion} from '../model/StudentQuestion';
import {ChoiceGroup, IChoiceGroupOption} from '@fluentui/react/lib/ChoiceGroup';
import { Label } from "@fluentui/react";
import { IChoiceGroupOption} from '@fluentui/react/lib/ChoiceGroup';
import { Checkbox, ICheckboxProps, Label, TextField } from "@fluentui/react";
import { getTheme } from '@fluentui/react';
import parse from 'html-react-parser';
interface StudentQuestionComponentProps {
question: StudentQuestion;
selectedOption: number;
setSelectedOption: (choice: number) => void;
selectedOption: string[];
setSelectedOption: (choice: string[]) => void;
}
var getDisplay = (x:string, texttype:string)=> {
console.log("text type is");
console.log(texttype);
if (texttype === "html" || texttype === "text/html"){
var parseDisplay = (x:string, texttype:string)=> {
if (texttype === "html" || texttype === "text/html" || texttype === "HTML"){
x = x.replaceAll('\\n','<br>')
return parse(x)
}
// Need to handle case where texttype is markdown
return x
}
export const StudentQuestionComponent = (
{question, selectedOption, setSelectedOption}: StudentQuestionComponentProps
) => {
const theme = getTheme();
const options: IChoiceGroupOption[] = question.options.map((value, index) => ({
key: index.toString(),
text: value,
}));
return (
<>
<br/>
<div style={{
backgroundColor: '#faf9f8',
width: '50%',
margin: 'auto',
padding: '10px',
boxShadow: theme.effects.elevation8
}}>
<br/>
<div style={{margin: '30px', textAlign: 'left'}}>
<Label style={{textAlign: 'left', fontSize: '25px'}}>Question</Label>
<p>{getDisplay(question.description, question.textType)}</p>
{console.log(question)}
<ChoiceGroup
selectedKey={selectedOption.toString()}
onChange={(_: any, option) => {
if (option) {
setSelectedOption(Number(option.key));
const shouldCheckBoxBeChecked = (index:number):boolean => {
if (index in selectedOption){
return true;
}
else{
return false;
}
}
const createOptions = (): ICheckboxProps[] => {
const createOneOption = (optionId: number): ICheckboxProps => {
return {
id: optionId.toString().concat(question.id) ,
defaultChecked: shouldCheckBoxBeChecked(optionId),
onChange: (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean) => {
var previousArray:string[] = Object.assign([], selectedOption)
if (ev?.currentTarget.id !== undefined){
if (checked){
if (previousArray.includes(optionId.toString()) === false){
previousArray.push(optionId.toString())
}
}
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>
<br/>
<br/>
</div>
</>
)
</>
)
}
}

Просмотреть файл

@ -187,8 +187,9 @@ export class FakeRepository implements IRepository {
description: "Applications of machine learning are all around us",
lastModified: new Date(),
options: ["True", "False"],
answer: 0,
answer: ['0'],
textType:"text",
questionType:"TF"
},
'1': {
id: '1',
@ -200,8 +201,9 @@ export class FakeRepository implements IRepository {
"The use of neural networks",
"Deep learning is used in robots",
],
answer: 1,
textType:"text"
answer: ['1'],
textType:"text",
questionType:"MCQ",
},
'2': {
id: '2',
@ -213,8 +215,9 @@ export class FakeRepository implements IRepository {
"To customise a shopping experience based on the type of the customer",
"Both of the above",
],
answer: 2,
textType:"text"
answer: ['2'],
textType:"text",
questionType:"MCQ"
},
'3': {
id: '3',
@ -227,8 +230,9 @@ export class FakeRepository implements IRepository {
"Documentations",
"All of the above",
],
answer: 3,
textType:"text"
answer: ['3'],
textType:"text",
questionType:"MCQ",
},
'4': {
id: '4',
@ -241,8 +245,9 @@ export class FakeRepository implements IRepository {
"System Models",
"Software Models",
],
answer: 1,
textType:"text"
answer: ['1'],
textType:"text",
questionType:"MCQ"
},
'5': {
id: '5',
@ -255,8 +260,9 @@ export class FakeRepository implements IRepository {
"3",
"5",
],
answer: 2,
textType:"text"
answer: ['2'],
textType:"text",
questionType:"MCQ"
},
'6': {
id: '6',
@ -269,8 +275,9 @@ export class FakeRepository implements IRepository {
"Increasing complexity",
"Self-regulation",
],
answer: 0,
textType:"text"
answer:['0'],
textType:"text",
questionType:"MCQ"
},
'7': {
id: '7',
@ -283,8 +290,9 @@ export class FakeRepository implements IRepository {
"Maintenance",
"All of the above",
],
answer: 3,
textType:"text"
answer: ['3'],
textType:"text",
questionType:"MCQ"
},
'8': {
id: '8',
@ -297,8 +305,9 @@ export class FakeRepository implements IRepository {
"Software Quality Management",
"All of the above",
],
answer: 3,
textType:"text"
answer: ['3'],
textType:"text",
questionType:"MCQ"
},
}
}
@ -363,7 +372,7 @@ export class FakeRepository implements IRepository {
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 {
return true;

Просмотреть файл

@ -22,7 +22,7 @@ export interface IRepository {
getAssessmentStats(assessmentId: string): Promise<AssessmentStatistics>;
getStudentAssessment(assessmentId: string): Promise<StudentAssessment>;
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;
deleteQuestionBanks(questionBankIds: string[]): void;
isReady(): boolean;

Просмотреть файл

@ -1,9 +1,23 @@
export interface Question {
id: string,
name: string,
description: string,
lastModified: Date,
options: string[],
answer: number,
textType:string,
export abstract class Question {
id: string;
name: string;
description: string;
lastModified: Date;
answer: 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 {
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 request: SubmitStudentAssessmentRequest = {
responses: Object.entries(chosenOptions).reduce((a, item) => ({

Просмотреть файл

@ -3,6 +3,7 @@ export interface StudentQuestion {
name: string,
description: string,
options: string[],
chosenOption: number,
chosenOption: string[],
textType:string,
questionType:string
}

Просмотреть файл

@ -7,14 +7,13 @@ import { Question } from "../Question";
export class GiftParser extends AssessmentAppParser{
public parse(): void {
var questionBankTitle:string = "Not defined yet";
var questions:Question[] = [];
const quiz: GIFTQuestion[] = parse(this.raw)
for (let question in quiz){
var q: GIFTQuestion = quiz[question]
console.log(q);
if (q.type === "Category"){
questionBankTitle = q.title;
}
@ -23,12 +22,13 @@ export class GiftParser extends AssessmentAppParser{
var choices:TextChoice[] = q.choices;
var answerTexts = Array();
var correctAnswer = 0;
var correctAnswer = [];
for (var choice in choices){
var details:TextChoice = choices[choice];
answerTexts.push(this.removeTags(details.text['text']));
if (details.isCorrect){
correctAnswer = +choice; // plus operator converts to number
var weight = details.weight
if (weight != null && weight > 0){
correctAnswer.push(choice); // plus operator converts to number
}
}
var stem:TextFormat = q.stem;
@ -39,7 +39,8 @@ export class GiftParser extends AssessmentAppParser{
lastModified: new Date (),
options: answerTexts,
answer: correctAnswer,
textType:stem.format
textType:stem.format,
questionType: "MCQ",
}
questions.push(question);
@ -54,13 +55,27 @@ export class GiftParser extends AssessmentAppParser{
description: stem.text,
lastModified: new Date (),
options: ["True", "False"],
answer: ans? 0:1,
textType:stem.format
answer: ans? ['0']:['1'],
textType:stem.format,
questionType: "TF",
}
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 = {

Просмотреть файл

@ -15,12 +15,12 @@ export class MicrosoftOSCParser extends AssessmentAppParser{
var questions:Question[] = [];
for (let question of rawQuestion.quiz){
var answerTexts = Array();
var correctAnswer = 0;
var correctAnswer = [];
var counter = 0;
for (let option of question.answerOptions){
answerTexts.push(option.answerText)
if (option.isCorrect == "true"){
correctAnswer = counter ;
correctAnswer.push(counter.toString());
}
counter = counter + 1;
@ -28,12 +28,13 @@ export class MicrosoftOSCParser extends AssessmentAppParser{
const questionToSave: Question = {
id: "",
name: question.questionText,
name: question.questionText,
description: question.questionText,
lastModified: new Date (),
lastModified: new Date(),
options: answerTexts,
answer: correctAnswer,
textType:"text"
textType: "text",
questionType: "MCQ"
}
questions.push(questionToSave);
}

Просмотреть файл

@ -2,6 +2,7 @@ import { ParsedQuestionBank } from "./ParsedQuestionBank";
import { AssessmentAppParser } from "./Parser";
import { Question } from "../Question";
// Currently only supports MCQs and TFs
export class OriginalAppParser extends AssessmentAppParser{
public parse() {
const rawData = JSON.parse(this.raw);
@ -13,10 +14,11 @@ export class OriginalAppParser extends AssessmentAppParser{
id: "",
name: rawQuestion.name,
description: rawQuestion.description,
lastModified: new Date (),
lastModified: new Date(),
options: rawQuestion.options,
answer: rawQuestion.answer,
textType: rawQuestion.textType
textType: rawQuestion.textType,
questionType: rawQuestion.questionType
}
questions.push(question);
}

Просмотреть файл

@ -20,6 +20,7 @@ export class AssessmentAppParser {
str = str.toString();
str = str.replace( /(<([^>]+)>)/ig, '')
str = str.replaceAll('\\n','')
str = str.replace(/\&nbsp;/g, "")
return str;}
}

Просмотреть файл

@ -3,10 +3,16 @@ import { ParsedQuestionBank } from "./ParsedQuestionBank";
import { AssessmentAppParser } from "./Parser";
import { Question } from "../Question";
interface questionTypeSpecificParse {
correctAnswer:string[]
options:string[]
};
export class QTIParser extends AssessmentAppParser{
options = {
ignoreAttributes:false
};
public parse() {
var questionBankTitle:string = "Not defined yet";
var questions:Question[] = [];
@ -19,40 +25,57 @@ export class QTIParser extends AssessmentAppParser{
for (let questionId in questionsSection){
var currQuestion = questionsSection[questionId];
// Get question title
var questionTitle = currQuestion['@_title'];
// Get question type
var qMetaDataField = currQuestion['itemmetadata']['qtimetadata']['qtimetadatafield'];
var metaData = qMetaDataField[0]; // 0 position contains question type
if (metaData['fieldentry'] != 'multiple_choice_question'){
continue; // As we currently only support MCQs
}
var questionType = 'NA';
// Get question description
var questionText = currQuestion['presentation']['material']['mattext']['#text'];
questionText = questionText.split('\n')[1];
// Get all options
var responseLabels = currQuestion['presentation']['response_lid']['render_choice']['response_label'];
var answerTexts = Array();
var correctAnswer = currQuestion['resprocessing']['respcondition']['conditionvar']['varequal']['#text']
for (let responseId in responseLabels){
var response = responseLabels[responseId];
if (correctAnswer == response['@_ident']){
correctAnswer = responseId;
}
answerTexts.push(this.removeTags(response['material']['mattext']['#text']))
// Get text type
var cleanedTextType:string = 'text'
if (currQuestion['presentation']['material']['mattext']['@_texttype'] === "text/html"){
cleanedTextType = "html";
}
// 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 = {
id: "",
name: questionTitle,
description: questionText,
lastModified: new Date (),
options: answerTexts,
answer: correctAnswer,
textType:currQuestion['presentation']['material']['mattext']['@_texttype'],
}
id: "",
name: questionTitle,
description: questionText,
lastModified: new Date(),
options: result.options,
answer: result.correctAnswer,
textType: cleanedTextType,
questionType: questionType
}
questions.push(question);
}
var qb: ParsedQuestionBank = {
questionBankTitle: questionBankTitle,
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 * as React from 'react';
import {useContext, useEffect, useState} from 'react';
import {StudentQuestionComponent} from '../../components/StudentQuestionComponent';
import {StudentQuestion} from '../../model/StudentQuestion';
@ -17,7 +16,7 @@ export const StudentQuiz = () => {
const {id} = useParams<StudentQuizParams>();
const repositoryContext = useContext(RepositoryContext);
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 [studentAssessment, setStudentAssessment] = useState<StudentAssessment | null>(null);
const history = useHistory();

Просмотреть файл

@ -20,8 +20,9 @@ export const NewQuestionPage = () => {
description: "",
lastModified: new Date(),
options: ['', ''],
answer: -1,
textType:""
answer: [],
textType:"",
questionType:"MCQ",
});
const {bankId} = useParams<NewQuestionPageParams>();
const repositoryContext = React.useContext(RepositoryContext);
@ -46,7 +47,7 @@ export const NewQuestionPage = () => {
padding: '10px',
boxShadow: theme.effects.elevation8
}}>
<EditQuestionComponent question={question} setQuestion={setQuestion}/>
<EditQuestionComponent question={question} setQuestion={setQuestion}/>
<Container style={{margin: '30px', position: 'relative'}}>
<Row>
<Col md={2}/>

Просмотреть файл

@ -21,18 +21,20 @@ export const QuestionPage = () => {
name: "",
description: "",
lastModified: new Date(),
options: ['', ''],
answer: -1,
textType:"text"
questionType: "",
answer: ['-1'],
textType:"text",
options:['', '']
});
const [savedQuestion, setSavedQuestion] = useState<Question>({
id: "",
name: "",
description: "",
lastModified: new Date(),
options: ['', ''],
answer: -1,
textType:"text"
questionType:"",
answer: [],
textType:"text",
options:['', '']
})
const {id} = useParams<QuestionPageParams>();
const repositoryContext = React.useContext(RepositoryContext);

Просмотреть файл

@ -105,7 +105,7 @@ export const QuestionBankPage = () => {
if (repositoryContext == null) {
return <p>Question Bank cannot be found</p>
}
const redirectToNewQuestion = () => {
const redirectToNewQuestion = () => {
history.push(`/spa/new-question/bank=${id}`);
}
const _items: ICommandBarItemProps[] = [