Refactored parsers
This commit is contained in:
Родитель
72407eeba0
Коммит
6a1d592a75
|
@ -3,11 +3,10 @@ import * as React from "react";
|
|||
import {useMemo, useState} from "react";
|
||||
import {RepositoryContext} from "../context/RepositoryContext";
|
||||
import { Dropdown, IDropdown } from '@fluentui/react/lib/Dropdown';
|
||||
import { IStackTokens, Stack } from '@fluentui/react/lib/Stack';
|
||||
import { parse, GIFTQuestion, TextChoice, TextFormat } from "gift-pegjs";
|
||||
import { arrayBuffer } from "stream/consumers";
|
||||
import { XMLParser} from "fast-xml-parser";
|
||||
import { addConsoleHandler } from "selenium-webdriver/lib/logging";
|
||||
import { AssessmentAppParserFactory } from "../model/parsers/AssessmentAppParserFactory";
|
||||
import { Question } from "../model/Question";
|
||||
import { ParsedQuestionBank } from "../model/parsers/ParsedQuestionBank";
|
||||
|
||||
const dropdownStyles = { dropdown: { width: 300 } };
|
||||
const modalPropsStyles = { main: { maxWidth: 600 } };
|
||||
|
@ -119,74 +118,71 @@ export const UploadQuestionBanksComponent = (
|
|||
return str;
|
||||
}
|
||||
|
||||
const giftFormat = async (rawData:any) => {
|
||||
// Only MCQ questions for now
|
||||
// ::title:: -> Question title
|
||||
// text -> Question text
|
||||
// [format] -> [html], [plain], [markdown]
|
||||
const quiz: GIFTQuestion[] = parse(rawData)
|
||||
console.log(quiz);
|
||||
const bank = await repositoryContext.createNewQuestionBank({
|
||||
id: "",
|
||||
name: "GIFT Question Bank", // We update the name later
|
||||
description: "",
|
||||
lastModified: new Date(),
|
||||
questionIds: [],
|
||||
assessmentType: "",
|
||||
});
|
||||
console.log("Created a new question bank");
|
||||
// const giftFormat = async (rawData:any) => {
|
||||
// // Only MCQ and True/False questions supported for now
|
||||
// const quiz: GIFTQuestion[] = parse(rawData)
|
||||
// console.log(quiz);
|
||||
// const bank = await repositoryContext.createNewQuestionBank({
|
||||
// id: "",
|
||||
// name: "GIFT Question Bank", // We update the name later
|
||||
// description: "",
|
||||
// lastModified: new Date(),
|
||||
// questionIds: [],
|
||||
// assessmentType: "",
|
||||
// });
|
||||
// console.log("Created a new question bank");
|
||||
|
||||
|
||||
for (let question in quiz){
|
||||
var q: GIFTQuestion = quiz[question]
|
||||
if (q.type === "Category"){
|
||||
await repositoryContext.updateQuestionBankWithName(bank.id, q.title)
|
||||
}
|
||||
if (q.type === "MC"){ // multiple choice
|
||||
// for (let question in quiz){
|
||||
// var q: GIFTQuestion = quiz[question]
|
||||
// if (q.type === "Category"){
|
||||
// await repositoryContext.updateQuestionBankWithName(bank.id, q.title)
|
||||
// }
|
||||
// if (q.type === "MC"){ // multiple choice
|
||||
|
||||
var choices:TextChoice[] = q.choices;
|
||||
var answerTexts = Array();
|
||||
var correctAnswer = 0;
|
||||
for (var choice in choices){
|
||||
var details:TextChoice = choices[choice];
|
||||
answerTexts.push(removeTags(details.text['text']));
|
||||
if (details.isCorrect){
|
||||
correctAnswer = +choice; // plus operator converts to number
|
||||
}
|
||||
}
|
||||
var stem:TextFormat = q.stem;
|
||||
// var choices:TextChoice[] = q.choices;
|
||||
// var answerTexts = Array();
|
||||
// var correctAnswer = 0;
|
||||
// for (var choice in choices){
|
||||
// var details:TextChoice = choices[choice];
|
||||
// answerTexts.push(removeTags(details.text['text']));
|
||||
// if (details.isCorrect){
|
||||
// correctAnswer = +choice; // plus operator converts to number
|
||||
// }
|
||||
// }
|
||||
// var stem:TextFormat = q.stem;
|
||||
|
||||
await repositoryContext.saveNewQuestion(bank.id, {
|
||||
id: "",
|
||||
name: removeTags(stem.text),
|
||||
description: removeTags(stem.text),
|
||||
lastModified: new Date (),
|
||||
options: answerTexts,
|
||||
answer: correctAnswer,
|
||||
})
|
||||
// await repositoryContext.saveNewQuestion(bank.id, {
|
||||
// id: "",
|
||||
// name: removeTags(stem.text),
|
||||
// description: removeTags(stem.text),
|
||||
// lastModified: new Date (),
|
||||
// options: answerTexts,
|
||||
// answer: correctAnswer,
|
||||
// })
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
if (q.type === "TF"){
|
||||
var stem:TextFormat = q.stem;
|
||||
var isTrue:boolean = q.isTrue;
|
||||
await repositoryContext.saveNewQuestion(bank.id, {
|
||||
id: "",
|
||||
name: removeTags(stem.text),
|
||||
description: removeTags(stem.text),
|
||||
lastModified: new Date (),
|
||||
options: ["True", "False"],
|
||||
answer: isTrue?0:1,
|
||||
})
|
||||
// if (q.type === "TF"){
|
||||
// var stem:TextFormat = q.stem;
|
||||
// var isTrue:boolean = q.isTrue;
|
||||
// await repositoryContext.saveNewQuestion(bank.id, {
|
||||
// id: "",
|
||||
// name: removeTags(stem.text),
|
||||
// description: removeTags(stem.text),
|
||||
// lastModified: new Date (),
|
||||
// options: ["True", "False"],
|
||||
// answer: isTrue?0:1,
|
||||
// })
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
return true;
|
||||
// return true;
|
||||
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
|
||||
const canvasFormat = async (rawData:any) => {
|
||||
|
@ -242,52 +238,6 @@ export const UploadQuestionBanksComponent = (
|
|||
|
||||
}
|
||||
|
||||
// for (let question in quiz){
|
||||
// var q: GIFTQuestion = quiz[question]
|
||||
// if (q.type === "Category"){
|
||||
// await repositoryContext.updateQuestionBankWithName(bank.id, q.title)
|
||||
// }
|
||||
// if (q.type === "MC"){ // multiple choice
|
||||
|
||||
// var choices:TextChoice[] = q.choices;
|
||||
// var answerTexts = Array();
|
||||
// var correctAnswer = 0;
|
||||
// for (var choice in choices){
|
||||
// var details:TextChoice = choices[choice];
|
||||
// answerTexts.push(removeTags(details.text['text']));
|
||||
// if (details.isCorrect){
|
||||
// correctAnswer = +choice; // plus operator converts to number
|
||||
// }
|
||||
// }
|
||||
// var stem:TextFormat = q.stem;
|
||||
|
||||
// await repositoryContext.saveNewQuestion(bank.id, {
|
||||
// id: "",
|
||||
// name: removeTags(stem.text),
|
||||
// description: removeTags(stem.text),
|
||||
// lastModified: new Date (),
|
||||
// options: answerTexts,
|
||||
// answer: correctAnswer,
|
||||
// })
|
||||
|
||||
// }
|
||||
|
||||
// if (q.type === "TF"){
|
||||
// var stem:TextFormat = q.stem;
|
||||
// var isTrue:boolean = q.isTrue;
|
||||
// await repositoryContext.saveNewQuestion(bank.id, {
|
||||
// id: "",
|
||||
// name: removeTags(stem.text),
|
||||
// description: removeTags(stem.text),
|
||||
// lastModified: new Date (),
|
||||
// options: ["True", "False"],
|
||||
// answer: isTrue?0:1,
|
||||
// })
|
||||
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
|
@ -306,29 +256,32 @@ export const UploadQuestionBanksComponent = (
|
|||
if (!text) {
|
||||
return;
|
||||
}
|
||||
var parserFactory = new AssessmentAppParserFactory(text.toString(), selectedOption.key);
|
||||
var parser = parserFactory.parser;
|
||||
parser.parse();
|
||||
var questionBanks: ParsedQuestionBank[] = parser.questionbanks;
|
||||
|
||||
switch(selectedOption.key) {
|
||||
case "A": {
|
||||
// opensource curriculum
|
||||
const rawData = JSON.parse(text.toString());
|
||||
await openSourceCurriculumJson(rawData);
|
||||
break;
|
||||
}
|
||||
case "B": {
|
||||
//statements;
|
||||
const rawData = JSON.parse(text.toString());
|
||||
await assessmentAppJson(rawData);
|
||||
break;
|
||||
}
|
||||
case "C": {
|
||||
await giftFormat(text.toString());
|
||||
break;
|
||||
}
|
||||
case "D": {
|
||||
await canvasFormat(text.toString());
|
||||
break;
|
||||
for (let qb_id in questionBanks){
|
||||
console.log("currenly looking at question bank");
|
||||
console.log(qb_id);
|
||||
var questionBank:ParsedQuestionBank = questionBanks[qb_id];
|
||||
const bank = await repositoryContext.createNewQuestionBank({
|
||||
id: "",
|
||||
name: questionBank.questionBankTitle,
|
||||
description: "",
|
||||
lastModified: new Date(),
|
||||
questionIds: [],
|
||||
assessmentType: "",
|
||||
});
|
||||
|
||||
const questions:Question[] = questionBank.questions;
|
||||
for (let questionId in questions){
|
||||
await repositoryContext.saveNewQuestion(bank.id, questions[questionId])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
onFinish(true);
|
||||
setInProgress(false);
|
||||
}
|
||||
|
@ -337,8 +290,8 @@ export const UploadQuestionBanksComponent = (
|
|||
|
||||
const dropdownRef = React.createRef<IDropdown>();
|
||||
const uploadOptions = [
|
||||
{ key: 'A', text: 'Microsoft Open Source Curriculum JSON'},
|
||||
{ key: 'B', text: 'Assessment App JSON' },
|
||||
{ key: 'A', text: 'Assessment App JSON'},
|
||||
{ key: 'B', text: 'Microsoft Open Source Curriculum JSON ' },
|
||||
{ key: 'C', text: 'GIFT Export'},
|
||||
{ key: 'D', text: 'QTI Zip Export' },
|
||||
];
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import { GiftParser } from "./GiftParser";
|
||||
import { MicrosoftOSCParser } from "./MicrosoftOSCParser";
|
||||
import { OriginalAppParser } from "./OriginalParser";
|
||||
import { AssessmentAppParser } from "./Parser";
|
||||
import { QTIParser } from "./QTIParser";
|
||||
|
||||
export class AssessmentAppParserFactory{
|
||||
// parser!: AssessmentAppParser;
|
||||
|
||||
raw:string;
|
||||
parser!: AssessmentAppParser;
|
||||
|
||||
constructor( raw:string, key:string) {
|
||||
this.raw = raw;
|
||||
|
||||
switch(key) {
|
||||
case "A": {
|
||||
// opensource curriculum
|
||||
this.parser = new OriginalAppParser(raw)
|
||||
break;
|
||||
}
|
||||
case "B": {
|
||||
//statements;
|
||||
this.parser = new MicrosoftOSCParser(raw)
|
||||
break;
|
||||
}
|
||||
case "C": {
|
||||
this.parser = new GiftParser(raw)
|
||||
break;
|
||||
}
|
||||
default : {
|
||||
this.parser = new QTIParser(raw)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import { parse, GIFTQuestion, TextChoice, TextFormat } from "gift-pegjs";
|
||||
import { ParsedQuestionBank } from "./ParsedQuestionBank";
|
||||
import { AssessmentAppParser } from "./Parser";
|
||||
import { Question } from "../Question";
|
||||
|
||||
|
||||
// Currently only parses in MCQs and TFs
|
||||
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]
|
||||
if (q.type === "Category"){
|
||||
questionBankTitle = q.title;
|
||||
}
|
||||
|
||||
if (q.type === "MC"){ // multiple choice
|
||||
|
||||
var choices:TextChoice[] = q.choices;
|
||||
var answerTexts = Array();
|
||||
var correctAnswer = 0;
|
||||
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 stem:TextFormat = q.stem;
|
||||
const question: Question = {
|
||||
id: "",
|
||||
name: this.removeTags(stem.text),
|
||||
description: this.removeTags(stem.text),
|
||||
lastModified: new Date (),
|
||||
options: answerTexts,
|
||||
answer: correctAnswer,
|
||||
}
|
||||
questions.push(question);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
var qb: ParsedQuestionBank = {
|
||||
questionBankTitle: questionBankTitle,
|
||||
questions:questions
|
||||
};
|
||||
this.questionbanks.push(qb);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import { ParsedQuestionBank } from "./ParsedQuestionBank";
|
||||
import { AssessmentAppParser } from "./Parser";
|
||||
import { Question } from "../Question";
|
||||
|
||||
// Currently only parses in MCQs and TFs
|
||||
export class MicrosoftOSCParser extends AssessmentAppParser{
|
||||
|
||||
public parse(): void {
|
||||
|
||||
const rawData = JSON.parse(this.raw);
|
||||
|
||||
for (let rawBank of rawData){
|
||||
|
||||
for (let rawQuestion of rawBank.quizzes) {
|
||||
var questions:Question[] = [];
|
||||
for (let question of rawQuestion.quiz){
|
||||
var answerTexts = Array();
|
||||
var correctAnswer = 0;
|
||||
var counter = 0;
|
||||
console.log("Read a new question");
|
||||
console.log(question.questionText);
|
||||
for (let option of question.answerOptions){
|
||||
answerTexts.push(option.answerText)
|
||||
if (option.isCorrect == "true"){
|
||||
correctAnswer = counter ;
|
||||
}
|
||||
counter = counter + 1;
|
||||
|
||||
}
|
||||
|
||||
const questionToSave: Question = {
|
||||
id: "",
|
||||
name: question.questionText,
|
||||
description: question.questionText,
|
||||
lastModified: new Date (),
|
||||
options: answerTexts,
|
||||
answer: correctAnswer,
|
||||
}
|
||||
questions.push(questionToSave);
|
||||
}
|
||||
|
||||
|
||||
var qb: ParsedQuestionBank = {
|
||||
questionBankTitle: rawQuestion.title,
|
||||
questions:questions
|
||||
};
|
||||
this.questionbanks.push(qb);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { ParsedQuestionBank } from "./ParsedQuestionBank";
|
||||
import { AssessmentAppParser } from "./Parser";
|
||||
import { Question } from "../Question";
|
||||
|
||||
export class OriginalAppParser extends AssessmentAppParser{
|
||||
public parse() {
|
||||
const rawData = JSON.parse(this.raw);
|
||||
for (let rawBank of rawData) {
|
||||
var questions:Question[] = [];
|
||||
for (let rawQuestion of rawBank.questions) {
|
||||
|
||||
const question: Question = {
|
||||
id: "",
|
||||
name: rawQuestion.name,
|
||||
description: rawQuestion.description,
|
||||
lastModified: new Date (),
|
||||
options: rawQuestion.options,
|
||||
answer: rawQuestion.answer,
|
||||
}
|
||||
questions.push(question);
|
||||
}
|
||||
|
||||
var qb: ParsedQuestionBank = {
|
||||
questionBankTitle: rawBank.name,
|
||||
questions:questions
|
||||
};
|
||||
this.questionbanks.push(qb);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { Question } from "../Question";
|
||||
|
||||
export type ParsedQuestionBank = {
|
||||
questionBankTitle:string;
|
||||
questions: Question[];
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { ParsedQuestionBank } from "./ParsedQuestionBank";
|
||||
|
||||
export class AssessmentAppParser {
|
||||
raw:string;
|
||||
questionbanks:ParsedQuestionBank[];
|
||||
|
||||
constructor( raw:string) {
|
||||
this.raw = raw;
|
||||
this.questionbanks = [];
|
||||
}
|
||||
|
||||
// This method gets overwritten by individual parsers
|
||||
public parse() {
|
||||
}
|
||||
|
||||
public removeTags(str:string) {
|
||||
if ((str===null) || (str===''))
|
||||
return '';
|
||||
else
|
||||
str = str.toString();
|
||||
str = str.replace( /(<([^>]+)>)/ig, '')
|
||||
str = str.replaceAll('\\n','')
|
||||
return str;}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import { XMLParser} from "fast-xml-parser";
|
||||
import { ParsedQuestionBank } from "./ParsedQuestionBank";
|
||||
import { AssessmentAppParser } from "./Parser";
|
||||
import { Question } from "../Question";
|
||||
|
||||
export class QTIParser extends AssessmentAppParser{
|
||||
options = {
|
||||
ignoreAttributes:false
|
||||
};
|
||||
public parse() {
|
||||
var questionBankTitle:string = "Not defined yet";
|
||||
var questions:Question[] = [];
|
||||
|
||||
const xmlParser: XMLParser = new XMLParser(this.options);
|
||||
var parsedInput = xmlParser.parse(this.raw);
|
||||
var assessment = parsedInput['questestinterop']['assessment'];
|
||||
questionBankTitle = assessment['@_title'];
|
||||
var questionsSection = assessment['section']['item']
|
||||
|
||||
for (let questionId in questionsSection){
|
||||
var currQuestion = questionsSection[questionId];
|
||||
var questionTitle = currQuestion['@_title'];
|
||||
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 questionText = currQuestion['presentation']['material']['mattext']['#text'];
|
||||
questionText = this.removeTags(questionText); // Clean any html tags
|
||||
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']))
|
||||
}
|
||||
// Finally creating the question
|
||||
const question:Question = {
|
||||
id: "",
|
||||
name: questionTitle,
|
||||
description: questionText,
|
||||
lastModified: new Date (),
|
||||
options: answerTexts,
|
||||
answer: correctAnswer,
|
||||
}
|
||||
questions.push(question);
|
||||
|
||||
}
|
||||
|
||||
var qb: ParsedQuestionBank = {
|
||||
questionBankTitle: questionBankTitle,
|
||||
questions:questions
|
||||
};
|
||||
this.questionbanks.push(qb);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Загрузка…
Ссылка в новой задаче