diff --git a/src/routes/print.js b/src/routes/print.js index ae909b2f5b6f8bed1586c8abc6e1b89362f5066d..8d1c0a8870c38bef93ee7c917d72426dda671740 100644 --- a/src/routes/print.js +++ b/src/routes/print.js @@ -1,16 +1,53 @@ -var express = require("express"); -const { buildPDFWithCallback } = require("../service/print/pdf"); const { buildDOCXWithCallback } = require('../service/print/docx') + +const { buildDOCX_1_WithCallback } = require('../service/print/printDocxV1.0/docx') + const requestMiddleware = require("../middlewares/request.middleware"); -// const base64 = require('base64topdf'); const BASE_URL = "/program/v1"; // Refactor this to move to service async function printDocx(req,res){ const id = req.query.id; const format = req.query.format; - // buildDOCXwithCallback(function (binary, error, errorMsg) { + const version = req.query.version; + + if(version === "1.0"){ + buildDOCX_1_WithCallback(id,function (binary, error, errorMsg,filename) { + var date = new Date(); + if (!error) { + if (format === "json") { + const resJSON = { + id: "api.collection.print", + ver: "1.0", + ts: date.toISOString(), + params: { + id, + format, + status: "successful", + err: null, + errmsg: null, + }, + responseCode: "OK", + result: { + content_id: id, + base64string: binary, + filename: filename + }, + }; + res.send(resJSON); + } else { + + res.setHeader('Content-Disposition', `attachment; filename=${filename}.docx`); + res.send(Buffer.from(binary, 'base64')); + } + } else { + res.status(404).send({ + error: errorMsg, + }); + } + + }); + } else { buildDOCXWithCallback(id,function (binary, error, errorMsg,filename) { - // console.log("Enttere dres") var date = new Date(); if (!error) { if (format === "json") { @@ -38,6 +75,7 @@ async function printDocx(req,res){ res.setHeader('Content-Disposition', `attachment; filename=${filename}.docx`); res.send(Buffer.from(binary, 'base64')); } + } else { res.status(404).send({ error: errorMsg, @@ -46,43 +84,9 @@ async function printDocx(req,res){ }); } -async function printPDF(req, res) { - const id = req.query.id; - const format = req.query.format; - buildPDFWithCallback(id, function (binary, error, errorMsg) { - var date = new Date(); - if (!error) { - if (format === "json") { - const resJSON = { - id: "api.collection.print", - ver: "1.0", - ts: date.toISOString(), - params: { - id, - format, - status: "successful", - err: null, - errmsg: null, - }, - responseCode: "OK", - result: { - content_id: id, - base64string: binary, - }, - }; - res.send(resJSON); - } else { - res.setHeader('Content-disposition', `attachment; filename=${id}.pdf`); - res.setHeader('Content-type', 'application/pdf'); - res.send(`data:application/pdf;base64, ${binary}`); - } - } else { - res.status(404).send({ - error: errorMsg, - }); - } - }); } + + module.exports = function (app) { app .route(BASE_URL + "/print/docx") @@ -90,6 +94,6 @@ module.exports = function (app) { requestMiddleware.gzipCompression(), requestMiddleware.createAndValidateRequestBody, printDocx - // printPDF ); -}; \ No newline at end of file + +}; diff --git a/src/service/print/dataImporter.js b/src/service/print/dataImporter.js index 50f3e4b9437ab86e1c938df8ce9116da1076537e..25187a6a4bdef70ad4575649045d5a52b8f8433b 100644 --- a/src/service/print/dataImporter.js +++ b/src/service/print/dataImporter.js @@ -1,14 +1,16 @@ -const fetch = require("node-fetch"); + const envVariables = require("../../envVariables"); +const fetch = require("node-fetch"); -class PDFDataImportError { + +class PrintDocxDataImportError { constructor(message) { this.message = message; - this.name = "PDFDataImportError"; + this.name = "PrintDocxDataImportError"; } } -function getItemsFromItemset(itemsetID,marks) { +function getItemsFromItemset(itemsetID, marks) { let status; const urlItemset = `${envVariables.baseURL}/action/itemset/v3/read/${itemsetID}`; return fetch(urlItemset) @@ -20,23 +22,29 @@ function getItemsFromItemset(itemsetID,marks) { if (status === 200) { if (r.result.itemset.items.length > 0) { const item = r.result.itemset.items[0]; - return getQuestionFromItem(item.identifier,marks); + return getQuestionFromItem(item.identifier, marks); } else { - throw new PDFDataImportError("Empty Itemset"); + throw new PrintDocxDataImportError("Empty Itemset"); } } else { - throw new PDFDataImportError( + throw new PrintDocxDataImportError( "Invalid Response for Itemset ID :: " + itemsetID ); } }) .catch((e) => { - if (e.name === "PDFDataImportError") throw e; - else throw new PDFDataImportError("Invalid Response for Itemset API"); + error = true; + if (e.name === "PrintDocxDataImportError"); + else e.message = "Uncaught Exception"; + let errorMsg = e.message; + return { + error, + errorMsg, + }; }); } -function getQuestionFromItem(itemID,marks) { +function getQuestionFromItem(itemID, marks) { let status; const urlItem = `${envVariables.baseURL}/action/assessment/v3/items/read/${itemID}`; return fetch(urlItem) @@ -47,19 +55,24 @@ function getQuestionFromItem(itemID,marks) { .then((r) => { if (status === 200) { if (r.result.assessment_item) { - r.result.assessment_item.marks = marks + r.result.assessment_item.marks = marks; return r.result.assessment_item; - } - else throw "Not a valid question"; + } else throw "Not a valid question"; } else { - throw new PDFDataImportError( + throw new PrintDocxDataImportError( "Invalid Response for Question ID :: " + itemID ); } }) .catch((e) => { - if (e.name === "PDFDataImportError") throw e; - else throw new PDFDataImportError("Invalid Response for Question API"); + error = true; + if (e.name === "PrintDocxDataImportError"); + else e.message = "Uncaught Exception"; + let errorMsg = e.message; + return { + error, + errorMsg, + }; }); } @@ -75,24 +88,22 @@ const getQuestionForSection = async (id) => { if (status === 200) { if (r.result.content.itemSets && r.result.content.itemSets.length > 0) { const itemset = r.result.content.itemSets[0]; - // console.log("Marks dataImports:", r.result.content.marks) const marks = r.result.content.marks; - // return { marks: marks, ...getItemsFromItemset(itemset.identifier) } - return getItemsFromItemset(itemset.identifier,marks); + return getItemsFromItemset(itemset.identifier, marks); } else { - throw new PDFDataImportError("Empty Section"); + throw new PrintDocxDataImportError("Empty Section"); } } else { - throw new PDFDataImportError( + throw new PrintDocxDataImportError( "Invalid Response for Hierarchy ID :: " + id ); } }) .catch((e) => { error = true; - if (e.name === "PDFDataImportError"); + if (e.name === "PrintDocxDataImportError"); else e.message = "Uncaught Exception"; - let errorMsg = e.message + let errorMsg = e.message; return { error, errorMsg, @@ -111,9 +122,8 @@ const getData = async (id) => { let sections; if (data && "children" in data) sections = data.children; else { - throw new PDFDataImportError("Invalid ID"); + throw new PrintDocxDataImportError("Invalid ID"); } - const questionIds = sections.map((section) => { if (section.children) return section.children @@ -131,12 +141,11 @@ const getData = async (id) => { sec.map((question) => getQuestionForSection(question).then((resp) => { if (resp.error) { - throw new PDFDataImportError(resp.errorMsg); + throw new PrintDocxDataImportError(resp.errorMsg); } else return resp; }) ) ); - const questionPromises = promiseMap.map((sectionPromise, index) => Promise.all(sectionPromise) .then((result) => result) @@ -162,7 +171,7 @@ const getData = async (id) => { .catch((e) => { console.log(e); error = true; - if (e.name === "PDFDataImportError") errorMsg = e.message; + if (e.name === "PrintDocxDataImportError") errorMsg = e.message; else errorMsg = "Uncaught Exception"; return { error, diff --git a/src/service/print/docx.js b/src/service/print/docx.js index 805b35dcc43ffef04165bbc81c860dda420295ff..2fda3a24aa890bc544ee0747d56146b26d77d403 100644 --- a/src/service/print/docx.js +++ b/src/service/print/docx.js @@ -1,28 +1,17 @@ const { getData } = require("./dataImporter"); const docx = require("docx"); +const fetch = require("node-fetch"); const { Packer } = docx; const getDocx = require("./getdocxdata"); -const fs = require("fs"); - var { - docDefinition, - getSectionTitle, - getTF, - getMTFHeader, -} = require("./utils/docDefinition"); -const ProgramServiceHelper = require("../../helpers/programHelper"); -var cheerio = require("cheerio"); -var cheerioTableparser = require("cheerio-tableparser"); -const sizeOf = require("image-size"); - -const programServiceHelper = new ProgramServiceHelper(); - -const { size, create, compact, result } = require("lodash"); -const { async } = require("rxjs/internal/scheduler/async"); -const { body } = require("express-validator"); + renderComprehension, + renderMCQ, + renderMTF, + renderQuestion, +} = require("./docxHelper"); const buildDOCXWithCallback = async (id, callback) => { - let error = false; + let error = false; let errorMsg = ""; let totalMarks = 0; getData(id) @@ -55,10 +44,6 @@ const buildDOCXWithCallback = async (id, callback) => { let questionCounter = 0; for (const d of data.sectionData) { - const sectionTitle = getSectionTitle( - d.section.name, - detectLanguage(d.section.name) - ); const section = d.section.name; let questionContent; questionContent = [{ sectionHeader: section, type: "section" }]; @@ -70,7 +55,7 @@ const buildDOCXWithCallback = async (id, callback) => { switch (question.category) { case "MCQ": questionContent = [ - await renderMCQ(question, questionCounter, question.marks), + await renderMCQ(question, questionCounter, question.marks,), ]; break; case "FTB": @@ -114,12 +99,13 @@ const buildDOCXWithCallback = async (id, callback) => { ]; break; case "MTF": - questionContent = await renderMTF( + questionContent = [await renderMTF( question, questionCounter, question.marks, "MTF" - ); + ), + ]; break; case "COMPREHENSION": questionContent = [ @@ -161,407 +147,6 @@ const buildDOCXWithCallback = async (id, callback) => { }); }; -const cleanHTML = (str, nbspAsLineBreak = false) => { - // Remove HTML characters since we are not converting HTML to PDF. - return str - .replace(/<[^>]+>/g, "") - .replace(/ /g, nbspAsLineBreak ? "\n" : ""); -}; - -const detectLanguage = (str) => { - const unicodeBlocks = [ - { - name: "Tamil", - regex: /[\u0B80-\u0BFF]+/g, - }, - { - name: "Hindi", - regex: /[\u0900-\u097F]+/g, - }, - ]; - - let language = "English"; - - const langSplit = { - Hindi: 0, - Tamil: 0, - English: 0, - Undefined: 0, - }; - if (typeof str === "string") { - str.split("").forEach((letter) => { - let found = false; - unicodeBlocks.forEach((block) => { - if (letter.match(block.regex)) { - langSplit[block.name]++; - found = true; - } - }); - if (!found) { - langSplit.English++; - } - }); - - let max = 0; - for (var key of Object.keys(langSplit)) { - if (langSplit[key] > max) { - max = langSplit[key]; - language = key; - } - } - - return language; - } - return "English"; -}; - -function createImageElement(src, width) { - let imageElement = {}; - if (src.search("image/gif") >= 0) return null; - imageElement.image = src; - let img = Buffer.from(src.split(";base64,").pop(), "base64"); - let dimensions = sizeOf(img); - let resizedWidth = dimensions.width * width; - imageElement.width = resizedWidth > 200 ? 200 : resizedWidth; - imageElement.height = - (dimensions.height / dimensions.width) * imageElement.width; - return imageElement; -} - -function extractTextFromElement(elem) { - let rollUp = ""; - if (cheerio.text(elem)) return cheerio.text(elem); - // if () - else if (elem.name === "sup") - return { text: elem.children[0].data, superScript: true }; - else if (elem.name === "sub") - return { text: elem.children[0].data, subScript: true }; - else if (elem.name === "strong") { - if (elem.children[0].data === undefined) { - return getStyleEle(elem); - } else { - return { text: elem.children[0].data, bold: true }; - } - } else if (elem.name === "i") { - if (elem.children[0].data === undefined) { - return getStyleEle(elem); - } else { - return { text: elem.children[0].data, italics: true }; - } - } else if (elem.name === "br") return { br: "break" }; - else if (elem.name === "u") { - if (elem.children[0].data === undefined) { - return getStyleEle(elem); - } else { - return { text: elem.children[0].data, underline: true }; - } - } else if (elem.type === "text" && elem.data) return elem.data; - else { - if (elem.children && elem.children.length) { - for (const nestedElem of elem.children) { - let recurse = extractTextFromElement(nestedElem); - if (Array.isArray(rollUp)) { - rollUp.push(recurse); - } else { - if (Array.isArray(recurse)) { - rollUp = recurse; - } else if (typeof recurse === "object") { - rollUp = [rollUp, recurse]; - } else rollUp += recurse; - } - } - } - } - return rollUp; -} - -function getStyleEle(el) { - let value = ""; - if ( - el.children[0].name && - (el.children[0].name === "i" || - el.children[0].name === "strong" || - el.children[0].name === "u") - ) { - return getStyleEle(el.children[0]); - } else { - if (el.children[0].data !== undefined) { - { - return ( - el.children[0] && - (el.children[0].data || - (el.children[0].children[0] && el.children[0].children[0].data)) - ); - } - } - } -} -async function getStack(htmlString, questionCounter) { - const stack = []; - $ = cheerio.load(htmlString); - const elems = $("body").children().toArray(); - for (const [index, elem] of elems.entries()) { - let nextLine = ""; - switch (elem.name) { - case "p": - let extractedText = extractTextFromElement(elem); - // Returns array if superscript/subscript inside - if (Array.isArray(extractedText)) nextLine = { text: extractedText }; - else nextLine += extractedText; - break; - case "ol": - nextLine = { - ol: elem.children.map((el) => { - return getStyleEle(el); - }), - }; - break; - - case "ul": - nextLine = { - ul: elem.children.map((el) => { - return getStyleEle(el); - }), - }; - break; - case "figure": - let { style } = elem.attribs; - let width = 1; - if (style) { - width = parseFloat(style.split(":").pop().slice(0, -2)); - width = width / 100; - } - if (elem.children && elem.children.length) { - let { src } = elem.children[0].attribs; - if (src) { - switch (src.slice(0, 4)) { - case "data": - nextLine = createImageElement(src, width); - break; - default: - let res = await programServiceHelper.getQuestionMedia(src); - nextLine = createImageElement(res, width); - } - } - } - if (!nextLine) - nextLine = "<An image of an unsupported format was scrubbed>"; - break; - } - if (index === 0 && questionCounter) { - if (elem.name === "p") { - if (typeof nextLine === "object") - nextLine = { text: [`${questionCounter}. `, ...nextLine.text] }; - else nextLine = `${questionCounter}. ${nextLine}`; - } else stack.push(`${questionCounter}.`); - } - stack.push(nextLine); - } - return stack; -} - -async function renderMCQ(question, questionCounter, marks) { - const questionOptions = [], - answerOptions = ["A", "B", "C", "D"]; - let questionTitle; - for (const [index, qo] of question.editorState.options.entries()) { - let qoBody = qo.value.body; - let qoData = - qoBody.search("img") >= 0 || - qoBody.search("sup") >= 0 || - qoBody.search("sub") >= 0 || - (qoBody.match(/<p>/g) && qoBody.match(/<p>/g).length >= 1) || - (qoBody.match(/<ol>/g) && qoBody.match(/<ol>/g).length >= 1) - ? await getStack(qoBody, answerOptions[index]) - : [`${answerOptions[index]}. ${cleanHTML(qoBody)}`]; - questionOptions.push(qoData); - } - let q = question.editorState.question; - questionTitle = - q.search("img") >= 0 || - q.search("sub") >= 0 || - q.search("sup") >= 0 || - (q.match(/<p>/g) && q.match(/<p>/g).length >= 1) || - (q.match(/<ol>/g) && q.match(/<ol>/g).length >= 1) - ? await getStack(q, questionCounter) - : [`${questionCounter}. ${cleanHTML(q)}`]; - - let questionOpt = []; - let imageProperties = []; - if (typeof questionOptions[0][1] === "object") { - questionOpt.push(questionOptions[0][0] + questionOptions[0][1].image); - imageProperties.push({ - width: questionOptions[0][1].width, - height: questionOptions[0][1].height, - }); - } else { - questionOpt.push(questionOptions[0][0]); - imageProperties.push({ - width: 0, - height: 0, - }); - } - - if (typeof questionOptions[1][1] === "object") { - questionOpt.push(questionOptions[1][0] + questionOptions[1][1].image); - imageProperties.push({ - width: questionOptions[1][1].width, - height: questionOptions[1][1].height, - }); - } else { - questionOpt.push(questionOptions[1][0]); - imageProperties.push({ - width: 0, - height: 0, - }); - } - - if (typeof questionOptions[2][1] === "object") { - questionOpt.push(questionOptions[2][0] + questionOptions[2][1].image); - imageProperties.push({ - width: questionOptions[2][1].width, - height: questionOptions[2][1].height, - }); - } else { - questionOpt.push(questionOptions[2][0]); - imageProperties.push({ - width: 0, - height: 0, - }); - } - - if (typeof questionOptions[3][1] === "object") { - questionOpt.push(questionOptions[3][0] + questionOptions[3][1].image); - imageProperties.push({ - width: questionOptions[3][1].width, - height: questionOptions[3][1].height, - }); - } else { - questionOpt.push(questionOptions[3][0]); - imageProperties.push({ - width: 0, - height: 0, - }); - } - - let data = { - Questions: questionTitle, - Option1: questionOpt[0], - Option2: questionOpt[1], - Option3: questionOpt[2], - Option4: questionOpt[3], - Marks: marks, - Language: detectLanguage(questionTitle[0]), - type: "MCQ", - height1: imageProperties[0].height, - width1: imageProperties[0].width, - height2: imageProperties[1].height, - width2: imageProperties[1].width, - height3: imageProperties[2].height, - width3: imageProperties[2].width, - height4: imageProperties[3].height, - width4: imageProperties[3].width, - }; - return data; -} - -async function renderQuestion(question, questionCounter, marks, Type) { - let data; - if ( - (question.media && question.media.length) || - question.editorState.question.search("img") >= 0 || - question.editorState.question.search("sub") >= 0 || - question.editorState.question.search("sup") >= 0 || - question.editorState.question.search("ul") >= 0 || - (question.editorState.question.match(/<p>/g) && - question.editorState.question.match(/<p>/g).length >= 1) || - (question.editorState.question.match(/<ol>/g) && - question.editorState.question.match(/<ol>/g).length >= 1) - ) { - data = await getStack(question.editorState.question, questionCounter); - } else { - data = [`${questionCounter}. ${cleanHTML(question.editorState.question)}`]; - } - - let quedata = { - Questions: data, - Marks: marks, - type: Type, - }; - return quedata; -} - -async function renderComprehension(question, questionCounter, marks, Type) { - let data; - if ( - (question.media && question.media.length) || - question.editorState.question.search("img") >= 0 || - question.editorState.question.search("sub") >= 0 || - question.editorState.question.search("sup") >= 0 || - question.editorState.question.search("ol") >= 0 || - question.editorState.question.search("ul") >= 0 || - (question.editorState.question.match(/<p>/g) && - question.editorState.question.match(/<p>/g).length >= 1) || - (question.editorState.question.match(/<ol>/g) && - question.editorState.question.match(/<ol>/g).length >= 1) - ) { - data = await getStack(question.editorState.question, questionCounter); - } else { - data = [`${questionCounter}. ${cleanHTML(question.editorState.question)}`]; - } - let quedata = { - Questions: data, - Marks: marks, - type: Type, - }; - return quedata; -} - -async function renderMTF(question, questionCounter, marks, Type) { - $ = cheerio.load(question.editorState.question); - cheerioTableparser($); - var data = []; - var columns = $("table").parsetable(false, false, false); - let transposeColumns = columns[0].map((_, colIndex) => - columns.map((row) => row[colIndex]) - ); - - const heading = questionCounter + ". " + cleanHTML($("p").first().text()); - data.push(heading, detectLanguage(heading), marks); - data.push( - getMTFHeader( - cleanHTML(transposeColumns[0][0]), - cleanHTML(transposeColumns[0][1]), - detectLanguage(cleanHTML(transposeColumns[0][0])) - ) - ); - - transposeColumns.shift(); - - const rows = []; - for (const r of transposeColumns) { - let left, right; - - if (r[0].search("img") >= 0) { - left = await getStack(r[0]); - } else left = [cleanHTML(r[0])]; - if (r[1].search("img") >= 0) { - right = await getStack(r[1]); - } else right = [cleanHTML(r[1])]; - rows.push({ left, right }); - } - - // return data.concat(rows); - let mtfData = [ - { - Questions: rows, - Marks: marks, - type: Type, - heading: heading, - }, - ]; - return mtfData; -} module.exports = { buildDOCXWithCallback, }; diff --git a/src/service/print/docxHelper.js b/src/service/print/docxHelper.js new file mode 100644 index 0000000000000000000000000000000000000000..8e70f03cd4a4717543e4fe97a33939c5eade53d9 --- /dev/null +++ b/src/service/print/docxHelper.js @@ -0,0 +1,482 @@ +var cheerio = require("cheerio"); +var cheerioTableparser = require("cheerio-tableparser"); +const sizeOf = require("image-size"); +const ProgramServiceHelper = require("../../helpers/programHelper"); +const programServiceHelper = new ProgramServiceHelper(); + +var { + docDefinition, + getSectionTitle, + getTF, + getMTFHeader, +} = require("./utils/docDefinition"); + +const cleanHTML = (str, nbspAsLineBreak = false) => { + // Remove HTML characters since we are not converting HTML to Docx. + if (str === undefined) return ""; + else + return str + .replace(/<[^>]+>/g, "") + .replace(/ /g, nbspAsLineBreak ? "\n" : ""); +}; + +const detectLanguage = (str) => { + const unicodeBlocks = [ + { + name: "Tamil", + regex: /[\u0B80-\u0BFF]+/g, + }, + { + name: "Hindi", + regex: /[\u0900-\u097F]+/g, + }, + ]; + + let language = "English"; + + const langSplit = { + Hindi: 0, + Tamil: 0, + English: 0, + Undefined: 0, + }; + if (typeof str === "string") { + str.split("").forEach((letter) => { + let found = false; + unicodeBlocks.forEach((block) => { + if (letter.match(block.regex)) { + langSplit[block.name]++; + found = true; + } + }); + if (!found) { + langSplit.English++; + } + }); + + let max = 0; + for (var key of Object.keys(langSplit)) { + if (langSplit[key] > max) { + max = langSplit[key]; + language = key; + } + } + + return language; + } + return "English"; +}; + +function createImageElement(src, width) { + let imageElement = {}; + if (src.search("image/gif") >= 0) return null; + imageElement.image = src; + let img = Buffer.from(src.split(";base64,").pop(), "base64"); + let dimensions = sizeOf(img); + let resizedWidth = dimensions.width * width; + imageElement.width = resizedWidth > 200 ? 200 : resizedWidth; + imageElement.height = + (dimensions.height / dimensions.width) * imageElement.width; + return imageElement; +} + +function extractTextFromElement(elem) { + let rollUp = ""; + if (cheerio.text(elem)) return cheerio.text(elem); + // if () + else if (elem.name === "sup") + return { text: elem.children[0].data, superScript: true }; + else if (elem.name === "sub") + return { text: elem.children[0].data, subScript: true }; + else if (elem.name === "strong") { + if (elem.children[0].data === undefined) { + return getStyleEle(elem); + } else { + return { text: elem.children[0].data, bold: true }; + } + } else if (elem.name === "i") { + if (elem.children[0].data === undefined) { + return getStyleEle(elem); + } else { + return { text: elem.children[0].data, italics: true }; + } + } else if (elem.name === "br") return { br: "break" }; + else if (elem.name === "u") { + if (elem.children[0].data === undefined) { + return getStyleEle(elem); + } else { + return { text: elem.children[0].data, underline: true }; + } + } else if (elem.type === "text" && elem.data) return elem.data; + else { + if (elem.children && elem.children.length) { + for (const nestedElem of elem.children) { + let recurse = extractTextFromElement(nestedElem); + if (Array.isArray(rollUp)) { + rollUp.push(recurse); + } else { + if (Array.isArray(recurse)) { + rollUp = recurse; + } else if (typeof recurse === "object") { + rollUp = [rollUp, recurse]; + } else rollUp += recurse; + } + } + } + } + return rollUp; +} + +function getStyleEle(el) { + let value = ""; + if ( + el.children[0].name && + (el.children[0].name === "i" || + el.children[0].name === "strong" || + el.children[0].name === "u") + ) { + return getStyleEle(el.children[0]); + } else { + if (el.children[0].data !== undefined) { + { + return ( + el.children[0] && + (el.children[0].data || + (el.children[0].children[0] && el.children[0].children[0].data)) + ); + } + } + } +} +async function getStack(htmlString) { + const stack = []; + $ = cheerio.load(htmlString); + const elems = $("body").children().toArray(); + for (const [index, elem] of elems.entries()) { + let nextLine = ""; + switch (elem.name) { + case "p": + let extractedText = extractTextFromElement(elem); + // Returns array if superscript/subscript inside + if (Array.isArray(extractedText)) nextLine = { text: extractedText }; + else nextLine += extractedText; + break; + case "ol": + nextLine = { + ol: elem.children.map((el) => { + return getStyleEle(el); + }), + }; + break; + + case "ul": + nextLine = { + ul: elem.children.map((el) => { + return getStyleEle(el); + }), + }; + break; + case "figure": + let { style } = elem.attribs; + let width = 1; + if (style) { + width = parseFloat(style.split(":").pop().slice(0, -2)); + width = width / 100; + } + if (elem.children && elem.children.length) { + let { src } = elem.children[0].attribs; + if (src) { + switch (src.slice(0, 4)) { + case "data": + nextLine = createImageElement(src, width); + break; + default: + let res = await programServiceHelper.getQuestionMedia(src); + nextLine = createImageElement(res, width); + } + } + } + if (!nextLine) + nextLine = "<An image of an unsupported format was scrubbed>"; + break; + } + stack.push(nextLine); + } + return stack; +} + +async function renderMCQ(question, questionCounter, marks) { + const questionOptions = [] + let questionTitle; + for (const [index, qo] of question.editorState.options.entries()) { + let qoBody = qo.value.body; + let qoData = + qoBody.search("img") >= 0 || + qoBody.search("sup") >= 0 || + qoBody.search("sub") >= 0 || + (qoBody.match(/<p>/g) && qoBody.match(/<p>/g).length >= 1) || + (qoBody.match(/<ol>/g) && qoBody.match(/<ol>/g).length >= 1) + ? await getStack(qoBody) + : [`${cleanHTML(qoBody)}`]; + questionOptions.push(qoData); + } + let q = question.editorState.question; + + questionTitle = + q.search("img") >= 0 || + q.search("sub") >= 0 || + q.search("sup") >= 0 || + (q.match(/<p>/g) && q.match(/<p>/g).length >= 1) || + (q.match(/<ol>/g) && q.match(/<ol>/g).length >= 1) + ? await getStack(q, questionCounter) + : [`${questionCounter}. ${cleanHTML(q)}`]; + + let questionOpt = []; + let imageProperties = []; + if (questionOptions[0] !== undefined) { + + if ( + questionOptions[0][0] !== undefined && + typeof questionOptions[0][0] === "object" + ) { + if( questionOptions[0][0].text){ + questionOpt.push( + ["I." , questionOptions[0][0].text[1]] + ); + } else{ + questionOpt.push( + ["I." , questionOptions[0][0].image] + ); + imageProperties.push({ + width: questionOptions[0][0].width, + height: questionOptions[0][0].height, + }); + } + + } else { + questionOpt.push(["I." , questionOptions[0][0]]); + imageProperties.push({ + width: 0, + height: 0, + }); + } + } + + if (questionOptions[1] !== undefined) { + if ( + questionOptions[1][0] !== undefined && + typeof questionOptions[1][0] === "object" + ) { + if( questionOptions[1][0].text){ + questionOpt.push( + [ "II." ,questionOptions[1][0].text[1]] + ); + } else{ + questionOpt.push( + ["II." , questionOptions[1][0].image] + ); + imageProperties.push({ + width: questionOptions[1][0].width, + height: questionOptions[1][0].height, + }); + } + + } else { + questionOpt.push(["II." ,questionOptions[1][0]]); + imageProperties.push({ + width: 0, + height: 0, + }); + } + } + + if (questionOptions[2] !== undefined) { + if ( + questionOptions[2][0] !== undefined && + typeof questionOptions[2][0] === "object" + ) { + if( questionOptions[2][0].text){ + questionOpt.push( + ["III." ,questionOptions[2][0].text[1]] + ); + } else { + questionOpt.push( + ["III." , questionOptions[2][0].image] + ); + imageProperties.push({ + width: questionOptions[2][0].width, + height: questionOptions[2][0].height, + }); + } + + } else { + questionOpt.push(["III." , questionOptions[2][0]]); + imageProperties.push({ + width: 0, + height: 0, + }); + } + } + + if (questionOptions[3] !== undefined) { + if ( + questionOptions[3][0] !== undefined && + typeof questionOptions[3][0] === "object" + ) { + + if( questionOptions[3][0].text){ + questionOpt.push( + ["IV." , questionOptions[3][0].text[1]] + ); + } else { + questionOpt.push( + ["IV." , questionOptions[3][0].image] + ); + imageProperties.push({ + width: questionOptions[3][0].width, + height: questionOptions[3][0].height, + }); + } + + } else { + questionOpt.push(["IV." ,questionOptions[3][0]]); + imageProperties.push({ + width: 0, + height: 0, + }); + } + } + + let data = { + QuestionIndex: questionCounter, + Questions: questionTitle, + Option1: questionOpt[0], + Option2: questionOpt[1], + Option3: questionOpt[2], + Option4: questionOpt[3], + Marks: marks, + Language: detectLanguage(questionTitle[0]), + type: "MCQ", + height1: imageProperties[0].height, + width1: imageProperties[0].width, + height2: imageProperties[1].height, + width2: imageProperties[1].width, + height3: imageProperties[2] ? imageProperties[2].height : undefined, + width3: imageProperties[2] ? imageProperties[2].width : undefined, + height4: imageProperties[3] ? imageProperties[3].height : undefined, + width4: imageProperties[3] ? imageProperties[3].width : undefined, + }; + return data; +} + +async function renderQuestion(question, questionCounter, marks, Type) { + let data; + $ = cheerio.load(question.editorState.question); + + cheerioTableparser($); + var columns = $("table").parsetable(false, false, false); + if (columns.length !== 0) { + return await renderMTF(question, questionCounter, marks, "MTF"); + } + if ( + (question.media && question.media.length) || + question.editorState.question.search("img") >= 0 || + question.editorState.question.search("sub") >= 0 || + question.editorState.question.search("sup") >= 0 || + question.editorState.question.search("ul") >= 0 || + (question.editorState.question.match(/<p>/g) && + question.editorState.question.match(/<p>/g).length >= 1) || + (question.editorState.question.match(/<ol>/g) && + question.editorState.question.match(/<ol>/g).length >= 1) + ) { + data = await getStack(question.editorState.question, questionCounter); + } else { + data = [`${questionCounter}. ${cleanHTML(question.editorState.question)}`]; + } + let quedata = { + QuestionIndex: questionCounter, + Questions: data, + Marks: marks, + type: Type, + }; + return quedata; +} + +async function renderComprehension(question, questionCounter, marks, Type) { + let data; + if ( + (question.media && question.media.length) || + question.editorState.question.search("img") >= 0 || + question.editorState.question.search("sub") >= 0 || + question.editorState.question.search("sup") >= 0 || + question.editorState.question.search("ol") >= 0 || + question.editorState.question.search("ul") >= 0 || + (question.editorState.question.match(/<p>/g) && + question.editorState.question.match(/<p>/g).length >= 1) || + (question.editorState.question.match(/<ol>/g) && + question.editorState.question.match(/<ol>/g).length >= 1) + ) { + data = await getStack(question.editorState.question, questionCounter); + } else { + data = [`${questionCounter}. ${cleanHTML(question.editorState.question)}`]; + } + let quedata = { + QuestionIndex: questionCounter, + Questions: data, + Marks: marks, + type: Type, + }; + return quedata; +} + +async function renderMTF(question, questionCounter, marks, Type) { + $ = cheerio.load(question.editorState.question); + + cheerioTableparser($); + var data = []; + var columns = $("table").parsetable(false, false, false); + let transposeColumns = columns[0].map((_, colIndex) => + columns.map((row) => row[colIndex]) + ); + + const heading = cleanHTML($("p").first().text()); + data.push(heading, detectLanguage(heading), marks); + data.push( + getMTFHeader( + cleanHTML(transposeColumns[0][0]), + cleanHTML(transposeColumns[0][1]), + detectLanguage(cleanHTML(transposeColumns[0][0])) + ) + ); + + transposeColumns.shift(); + + const rows = []; + for (const r of transposeColumns) { + let left, right; + if (r[0].search("img") >= 0) { + left = await getStack(r[0]); + } else left = [cleanHTML(r[0])]; + if (r[1].search("img") >= 0) { + right = await getStack(r[1]); + } else right = [cleanHTML(r[1])]; + rows.push({ left, right }); + } + + let mtfData = { + QuestionIndex: questionCounter, + Questions: rows, + Marks: marks, + type: Type, + heading: heading, + }; + + return mtfData; +} + +module.exports = { + renderComprehension, + renderMCQ, + renderMTF, + renderQuestion, +}; diff --git a/src/service/print/getdocxdata.js b/src/service/print/getdocxdata.js index 9c55f8a5510802dc3d684af077b5e4627fc025f7..d2d8e61bdd16c2796d26a01cdab5b5b6d2cf91e0 100644 --- a/src/service/print/getdocxdata.js +++ b/src/service/print/getdocxdata.js @@ -108,9 +108,16 @@ function create(data, paperData) { let page = 1; if (question[0].type === "COMPREHENSION") { let count = 0; - arr.push(Marks(question)); question[0].Questions.map((item) => { - arr.push(createSAObject(item, count++)); + arr.push( + formatview( + item, + count, + question[0].QuestionIndex, + question[0].Marks + ) + ); + count++; }); arr.push( @@ -120,9 +127,16 @@ function create(data, paperData) { ); } else if (question[0].type === "CuriosityQuestion") { let count = 0; - arr.push(Marks(question)); question[0].Questions.map((item) => { - arr.push(createSAObject(item, count++)); + arr.push( + formatview( + item, + count, + question[0].QuestionIndex, + question[0].Marks + ) + ); + count++; }); arr.push( @@ -132,9 +146,16 @@ function create(data, paperData) { ); } else if (question[0].type === "SA") { let count = 0; - arr.push(Marks(question)); question[0].Questions.map((item) => { - arr.push(createSAObject(item, count++)); + arr.push( + formatview( + item, + count, + question[0].QuestionIndex, + question[0].Marks + ) + ); + count++; }); arr.push( @@ -144,9 +165,16 @@ function create(data, paperData) { ); } else if (question[0].type === "LA") { let count = 0; - arr.push(Marks(question)); question[0].Questions.map((item) => { - arr.push(createSAObject(item, count)); + arr.push( + formatview( + item, + count, + question[0].QuestionIndex, + question[0].Marks + ) + ); + count++; }); arr.push( new Paragraph({ @@ -155,9 +183,16 @@ function create(data, paperData) { ); } else if (question[0].type === "VSA") { let count = 0; - arr.push(Marks(question)); question[0].Questions.map((item) => { - arr.push(createSAObject(item, count)); + arr.push( + formatview( + item, + count, + question[0].QuestionIndex, + question[0].Marks + ) + ); + count++; }); arr.push( new Paragraph({ @@ -166,13 +201,17 @@ function create(data, paperData) { ); } else if (question[0].type === "FTB") { let count = 0; - arr.push(Marks(question)); question[0].Questions.map((item) => { - if (typeof item === "object") { - arr.push(createFTBObject(item)); - } else { - arr.push(createFTB(item, count++)); - } + arr.push( + formatview( + item, + count, + question[0].QuestionIndex, + question[0].Marks + ) + ); + count++; + }); arr.push( new Paragraph({ @@ -181,10 +220,17 @@ function create(data, paperData) { ); } else if (question[0].type === "MCQ") { let testimage = formatOptions(question[0]); - arr.push(Marks(question)); let count = 0; question[0].Questions.map((item) => { - arr.push(createSAObject(item, count)); + arr.push( + formatview( + item, + count, + question[0].QuestionIndex, + question[0].Marks + ) + ); + count++; }); arr.push( new Paragraph({ @@ -198,16 +244,22 @@ function create(data, paperData) { }) ); } else if (question[0].type === "MTF") { - arr.push(Marks(question)); - let count = 0; - arr.push(createSAObject(question[0].heading, count)); + arr.push( + formatview( + question[0].heading, + count, + question[0].QuestionIndex, + question[0].Marks + ) + ); arr.push( new Paragraph({ children: [], // Just newline without text }) ); - arr.push(MTFTabel(question)); + + arr.push(mtfTableData(question)); arr.push( new Paragraph({ children: [], // Just newline without text @@ -267,13 +319,13 @@ function displayMTFHeader(data) { return new TableCell({ borders: MTFborder, width: { - size: 100 / 2, - type: WidthType.PERCENTAGE, + size: 4535, + type: WidthType.DXA, }, margins: { top: convertInchesToTwip(0.0693701), bottom: convertInchesToTwip(0.0693701), - left: convertInchesToTwip(0.0693701), + left: convertInchesToTwip(0.3493701), right: convertInchesToTwip(0.0693701), }, verticalAlign: VerticalAlign.CENTER, @@ -290,13 +342,13 @@ function displayMTFData(data) { return new TableCell({ borders: MTFborder, width: { - size: 100 / 2, - type: WidthType.PERCENTAGE, + size: 4535, + type: WidthType.DXA, }, margins: { top: convertInchesToTwip(0.0693701), bottom: convertInchesToTwip(0.0693701), - left: convertInchesToTwip(0.0693701), + left: convertInchesToTwip(0.3493701), right: convertInchesToTwip(0.0693701), }, verticalAlign: VerticalAlign.CENTER, @@ -313,12 +365,13 @@ function MTFTabel(question) { question[0].Questions.map((item) => { arr.push( new TableRow({ + children: [displayMTFData(item.left[0]), displayMTFData(item.right[0])], }) ); }); return new Table({ - columnWidths: [4505, 4505], + columnWidths: [5000, 5000], rows: arr, }); } @@ -588,6 +641,29 @@ const MTFborder = { size: 2, }, }; + +const MCQborder = { + left: { + style: BorderStyle.NONE, + size: 0, + color: "ffffff", + }, + right: { + style: BorderStyle.NONE, + size: 0, + color: "ffffff", + }, + top: { + style: BorderStyle.NONE, + size: 0, + color: "ffffff", + }, + bottom: { + style: BorderStyle.NONE, + size: 0, + color: "ffffff", + }, +}; function headers(text1, text2) { return new Table({ columnWidths: [4505, 4505], @@ -637,43 +713,65 @@ function headers(text1, text2) { } function displayNumber(data) { - if (typeof data === "object") { - return new TableCell({ - borders: MTFborder, - width: { - size: 300, - type: WidthType.DXA, - }, - margins: { - top: convertInchesToTwip(0.0693701), - bottom: convertInchesToTwip(0.0693701), - left: convertInchesToTwip(0.0693701), - right: convertInchesToTwip(0.0693701), - }, - verticalAlign: VerticalAlign.CENTER, - children: [ - new Paragraph({ - text: data.text[0].substr(0, 1), - }), - ], - }); + if (data !== undefined) { + if (typeof data === "object") { + return new TableCell({ + borders: MCQborder, + width: { + size: 650, + type: WidthType.DXA, + }, + margins: { + top: convertInchesToTwip(0.0693701), + bottom: convertInchesToTwip(0.0693701), + left: convertInchesToTwip(0.3493701), + right: convertInchesToTwip(0.0693701), + }, + verticalAlign: VerticalAlign.CENTER, + children: [ + new Paragraph({ + text: data[0], + }), + ], + }); + } else { + return new TableCell({ + borders: MCQborder, + width: { + size: 650, + type: WidthType.DXA, + }, + margins: { + top: convertInchesToTwip(0.0693701), + bottom: convertInchesToTwip(0.0693701), + left: convertInchesToTwip(0.3493701), + right: convertInchesToTwip(0.0693701), + }, + verticalAlign: VerticalAlign.CENTER, + children: [ + new Paragraph({ + text: data[0], + }), + ], + }); + } } else { return new TableCell({ - borders: MTFborder, + borders: MCQborder, width: { - size: 300, + size: 650, type: WidthType.DXA, }, margins: { top: convertInchesToTwip(0.0693701), bottom: convertInchesToTwip(0.0693701), - left: convertInchesToTwip(0.0693701), + left: convertInchesToTwip(0.3493701), right: convertInchesToTwip(0.0693701), }, verticalAlign: VerticalAlign.CENTER, children: [ new Paragraph({ - text: data.substr(0, 1), + text: "", }), ], }); @@ -682,22 +780,22 @@ function displayNumber(data) { function displayOptionsObject(data, count) { const arr = []; + if (data.text) { - data.text + if(typeof data === "object") { + arr.push(new TextRun(data)); + } else{ + data.text .map((text) => { if (typeof text === "object") { arr.push(new TextRun(text)); - } else { - // arr.push( - // new TextRun({ - // text: `${text}`, - // }) - // ); - } + } }) .reduce((prev, curr) => prev.concat(curr), []); + } + return new TableCell({ - borders: MTFborder, + borders: MCQborder, width: { size: 4505, type: WidthType.DXA, @@ -718,57 +816,155 @@ function displayOptionsObject(data, count) { } } function displayOptions(option, height, width) { - if (typeof option === "object") { - return displayOptionsObject(option); - } else if (option.includes("data:image/")) { - let image = getBufferData(option); + + if (option !== undefined) { + if (typeof option[1] === "object") { + return displayOptionsObject(option[1]); + } else if (option[1].includes("data:image/")) { + let image = getBufferImg(option[1]); + return new TableCell({ + borders: MCQborder, + width: { + size: 4505, + type: WidthType.DXA, + }, + margins: { + top: convertInchesToTwip(0.0693701), + bottom: convertInchesToTwip(0.0693701), + left: convertInchesToTwip(0.0693701), + right: convertInchesToTwip(0.0693701), + }, + verticalAlign: VerticalAlign.CENTER, + children: [ + new Paragraph({ + children: [ + new ImageRun({ + data: image, + transformation: { + width: width, + height: height, + }, + }), + ], + }), + ], + }); + } else { + return new TableCell({ + borders: MCQborder, + width: { + size: 4505, + type: WidthType.DXA, + }, + margins: { + top: convertInchesToTwip(0.0693701), + bottom: convertInchesToTwip(0.0693701), + left: convertInchesToTwip(0.0693701), + right: convertInchesToTwip(0.0693701), + }, + verticalAlign: VerticalAlign.CENTER, + children: [ + new Paragraph({ + children: [ + new TextRun({ + text: option[1], + }), + ], + }), + ], + }); + } + } else { return new TableCell({ - borders: MTFborder, + borders: MCQborder, width: { - size: 4505, + size: 650, type: WidthType.DXA, }, margins: { top: convertInchesToTwip(0.0693701), bottom: convertInchesToTwip(0.0693701), - left: convertInchesToTwip(0.0693701), + left: convertInchesToTwip(0.3493701), right: convertInchesToTwip(0.0693701), }, verticalAlign: VerticalAlign.CENTER, children: [ new Paragraph({ + text: "", + }), + ], + }); + } +} +function displayViewData(data) { + return new TableCell({ + borders: MCQborder, + width: { + size: 8000, + type: WidthType.DXA, + }, + verticalAlign: VerticalAlign.LEFT, + children: [createSAObject(data, 0)], + }); +} + +function displayQueNum(data) { + return new TableCell({ + borders: MCQborder, + width: { + size: 500, + type: WidthType.DXA, + }, + verticalAlign: VerticalAlign.LEFT, + children: [createSAObject(data, 0)], + }); +} + +function displayMarks(data) { + return new TableCell({ + borders: MCQborder, + width: { + size: 500, + type: WidthType.DXA, + }, + margins: { + left: convertInchesToTwip(0.3493701), + }, + verticalAlign: VerticalAlign.LEFT, + children: [createSAObject(data, 0)], + }); +} +function formatview(data, count, questionCounter, marks) { + if (count === 0) { + return new Table({ + borders: MCQborder, + columnWidths: [300, 10000, 300], + rows: [ + new TableRow({ + indent: { + left: 100, + }, children: [ - new ImageRun({ - data: image, - transformation: { - width: width, - height: height, - }, - }), + displayQueNum(questionCounter), + displayViewData(data), + displayMarks(marks), ], }), ], }); } else { - return new TableCell({ - borders: MTFborder, - width: { - size: 4505, - type: WidthType.DXA, - }, - margins: { - top: convertInchesToTwip(0.0693701), - bottom: convertInchesToTwip(0.0693701), - left: convertInchesToTwip(0.0693701), - right: convertInchesToTwip(0.0693701), - }, - verticalAlign: VerticalAlign.CENTER, - children: [ - new Paragraph({ + return new Table({ + borders: MCQborder, + columnWidths: [300, 10000, 300], + rows: [ + new TableRow({ + indent: { + left: 100, + }, children: [ - new TextRun({ - text: option.substr(2), - }), + displayQueNum(""), + displayViewData(data), + displayMarks(""), ], }), ], @@ -776,14 +972,29 @@ function displayOptions(option, height, width) { } } +function mtfTableData(data) { + const cell = new TableCell({ + children: [MTFTabel(data)], + }); + + return new Table({ + borders: MCQborder, + columnWidths: [300, 10000, 300], + rows: [ + new TableRow({ + children: [displayQueNum(""), cell, displayMarks("")], + }), + ], + }); +} + function optionsTabel(testimage) { return new Table({ + borders: MCQborder, columnWidths: [4505, 4505], rows: [ new TableRow({ - indent: { - left: 200, - }, + children: [ displayNumber(testimage[0]), @@ -796,7 +1007,7 @@ function optionsTabel(testimage) { }), new TableRow({ indent: { - left: 200, + left: 600, }, children: [ displayNumber(testimage[2]), diff --git a/src/service/print/printDocxV1.0/dataImporter.js b/src/service/print/printDocxV1.0/dataImporter.js new file mode 100644 index 0000000000000000000000000000000000000000..873193e00ea87ddcf91b6c806f9c7f17c2f08aaf --- /dev/null +++ b/src/service/print/printDocxV1.0/dataImporter.js @@ -0,0 +1,106 @@ +const { async } = require("rxjs/internal/scheduler/async"); +const fetch = require("node-fetch"); +const axios = require("axios"); + +const envVariables = require("../../../envVariables"); +const { result } = require("lodash"); + +class PrintDocx1ImportError { + constructor(message) { + this.message = message; + this.name = "PrintDocx1ImportError"; + } +} +const fields = + "body,primaryCategory,mimeType,qType,answer,templateId,responseDeclaration,interactionTypes,interactions,name,solutions,editorState,media,name,board,medium,gradeLevel,subject,topic,learningOutcome,maxScore,bloomsLevel,author,copyright,license"; + +const QUE_READ_URL= `${envVariables.SUNBIRD_ASSESSMENT_SERVICE_BASE_URL}question/v4/read/`; +const QS_HIERARCHY_READ_URL = `${envVariables.SUNBIRD_ASSESSMENT_SERVICE_BASE_URL}questionset/v4/hierarchy/`; + + +const getQuestionForSet = async (id) => { + let url= `${QUE_READ_URL}${id}?fields=${fields}` + const request = { + url: url, + method: "get", + }; + + return axios(request).then((r) => { + return r.data.result.question; + }).catch((e)=>{ + return{ + error:true, + errormsg:"wrong ID" + } + }) +}; +const getQuestionSet = async (id) => { + const headers = { + Authorization: "Bearer " + envVariables.SUNBIRD_PORTAL_API_AUTH_TOKEN + }; + let url= `${QS_HIERARCHY_READ_URL}${id}?mode=edit` + const request = { + url: url, + method: "get", + headers, + }; + + return axios(request).then((r) => { + const data = r.data.result.questionSet; + + let sections; + if (data && "children" in data) sections = data.children; + else { + throw new PrintDocx1ImportError("Invalid ID"); + } + const questionIds = sections.map((section) => { + if (section.children) + return section.children + .filter( + (child) => + data.acceptedContributions && + data.acceptedContributions.indexOf(child.identifier) > -1 + ) + .map((child) => { + return child.identifier; + }); + else return []; + }); // Hierarchy + + const promiseMap = questionIds.map((sec) => + sec.map((question) => { + if (question !== undefined) { + return getQuestionForSet(question).then((resp) => { + return resp; + }); + } + }) + ); + + const questionPromises = promiseMap.map((sectionPromise, index) => + Promise.all(sectionPromise) + .then((result) => result) + .catch((e) => { + throw e; + }) + ); + + return Promise.all(questionPromises).then((results) => { + const sectionData = results.map((questions, index) => { + return { + section: sections[index], + questions: questions, + }; + }); + + return { + sectionData, + paperData: data, + }; + }); + }); +}; + +module.exports = { + getQuestionSet, +}; diff --git a/src/service/print/printDocxV1.0/docx.js b/src/service/print/printDocxV1.0/docx.js new file mode 100644 index 0000000000000000000000000000000000000000..30bebed94d7195504f347c5f526b6ef6a81e5030 --- /dev/null +++ b/src/service/print/printDocxV1.0/docx.js @@ -0,0 +1,152 @@ +const { getQuestionSet } = require("./dataImporter"); +const docx = require("docx"); +const { Packer } = docx; +const getDocx = require("../getdocxdata"); + +var { + renderComprehension, + renderMCQ, + renderMTF, + renderQuestion, +} = require("../docxHelper"); + +const buildDOCX_1_WithCallback = async (id, callback) => { + let error = false; + let errorMsg = ""; + let totalMarks = 0; + getQuestionSet(id) + .then(async (data) => { + if (data.error) { + callback(null, data.error, data.errorMsg); + } else { + let subject, grade, examName, instructions, language, description; + if (data.paperData) { + subject = data.paperData.subject && data.paperData.subject[0]; + grade = data.paperData.gradeLevel && data.paperData.gradeLevel[0]; + examName = data.paperData.name; + instructions = data.paperData.description; + language = data.paperData.medium && data.paperData.medium[0]; + } + + const questionPaperContent = []; + const paperDetails = { + examName: examName, + className: grade, + subject: subject, + instructions: instructions.split(/\n/), + }; + let questionCounter = 0; + + for (const d of data.sectionData) { + const section = d.section.name; + let questionContent; + questionContent = [{ sectionHeader: section, type: "section" }]; + questionPaperContent.push(questionContent); + + for (const [index, question] of d.questions.entries()) { + //later remove this if condition + if (question !== undefined) { + + questionCounter += 1; + + switch (question.qType) { + case "MCQ": + questionContent = [ + await renderMCQ( + question, + questionCounter, + question.maxScore + ), + ]; + break; + case "FTB": + questionContent = [ + await renderQuestion( + question, + questionCounter, + question.maxScore, + "FTB" + ), + ]; + break; + case "SA": + questionContent = [ + await renderQuestion( + question, + questionCounter, + question.maxScore, + "SA" + ), + ]; + break; + case "LA": + questionContent = [ + await renderQuestion( + question, + questionCounter, + question.maxScore, + "LA" + ), + ]; + break; + case "VSA": + questionContent = [ + await renderQuestion( + question, + questionCounter, + question.maxScore, + "VSA" + ), + ]; + break; + case "MTF": + questionContent = await renderMTF( + question, + questionCounter, + question.maxScore, + "MTF" + ); + break; + case "COMPREHENSION": + questionContent = [ + await renderComprehension( + question, + questionCounter, + question.maxScore, + "COMPREHENSION" + ), + ]; + break; + case "CuriosityQuestion": + questionContent = [ + await renderQuestion( + question, + questionCounter, + question.maxScore, + "CuriosityQuestion" + ), + ]; + break; + } + } + questionPaperContent.push(questionContent); + } + } + const doc = await getDocx.create(questionPaperContent, paperDetails); + + const b64string = await Packer.toBase64String(doc); + let filename = grade + "_" + subject + "_" + examName; + filename = filename.replace(/\s/g, ""); + callback(b64string, null, null, filename); + } + }) + .catch((e) => { + error = true; + errorMsg = ""; + callback(null, error, errorMsg); + }); +}; + +module.exports = { + buildDOCX_1_WithCallback, +}; diff --git a/src/test/service/printService.js b/src/test/service/printService.js index 62b901cd0bed54cdd3881688624ce678e9fc55c0..fc3189d29c3d8c7b885451a074c43ec3a9e04dc6 100644 --- a/src/test/service/printService.js +++ b/src/test/service/printService.js @@ -2,8 +2,6 @@ process.env.NODE_ENV = "test"; const envVariables = require("../../envVariables"); const chai = require("chai"); -const nock = require("nock"); -const moment = require("moment"); const chaiHttp = require("chai-http"); chai.use(chaiHttp); const { expect } = chai; @@ -11,24 +9,38 @@ chai.use(require("chai-sorted")); chai.use(require("chai-match")); const _ = require("lodash"); -const programData = require("../testData/program.json"); -const dummyData = require("../testData/dummyData"); - -const BASE_URL = "/program/v1"; var rewire = require("rewire"); const { getData } = require("../../service/print/dataImporter"); const dataImporter = rewire("../../service/print/dataImporter.js"); -const pdf = rewire("../../service/print/pdf.js"); +const dataImporter1 = rewire( + "../../service/print/printDocxV1.0/dataImporter.js" +); + +const docx = rewire("../../service/print/docx.js"); +const docx1 = rewire("../../service/print/printDocxV1.0/docx.js"); const getQuestionForSection = dataImporter.__get__("getQuestionForSection"); const getItemsFromItemset = dataImporter.__get__("getItemsFromItemset"); const getQuestionFromItem = dataImporter.__get__("getQuestionFromItem"); - +const getQuestionSet = dataImporter1.__get__("getQuestionSet"); +const getQuestionForSet = dataImporter1.__get__("getQuestionForSet"); var cheerio = require("cheerio"); var cheerioTableparser = require("cheerio-tableparser"); + // eslint-disable-next-line no-undef describe("Print Service", () => { + + it("[Integration test] should output error for wrong heirarchy ID", (done) => { + getQuestionForSection("test").then((res) => { + expect(res.error).to.equal(true); + expect(res.errorMsg).to.equal( + "Invalid Response for Hierarchy ID :: test" + ); + done(); + }); + }); + it("[Integration test] should output error for wrong heirarchy ID", (done) => { getQuestionForSection("test").then((res) => { expect(res.error).to.equal(true); @@ -40,7 +52,7 @@ describe("Print Service", () => { }); it("[Integration test] should respond with question data for correct ID", (done) => { - getQuestionForSection("do_1132132525993082881105") + getQuestionForSection("do_11341784380642918411536") .then((response) => { expect(response).to.not.be.undefined; expect(response.itemType).to.equal("UNIT"); @@ -53,7 +65,7 @@ describe("Print Service", () => { }); it("[Integration test] should get Items from itemset for correct itemset ID", (done) => { - getItemsFromItemset("do_113213256596070400127") + getItemsFromItemset("do_1134178438144573441157") .then((response) => { expect(response).to.not.be.undefined; done(); @@ -63,32 +75,34 @@ describe("Print Service", () => { }); }); - it("[Integration test] should throw PDFDataImportError from itemset for incorrect itemset ID", (done) => { + it("[Integration test] should throw DocxDataImportError from itemset for incorrect itemset ID", (done) => { getItemsFromItemset("any") .then((response) => { - expect(response).to.not.be.undefined; + expect(response.error).to.be.equal(true); + done(); }) .catch((e) => { - expect(e.name).to.equal("PDFDataImportError"); + expect(e.name).to.equal("DocxDataImportError"); expect(e.message).to.equal("Invalid Response for Itemset ID :: any"); done(); }); }); - it("[Integration test] should throw PDFDataImportError for incorrect item ID", (done) => { + it("[Integration test] should throw DocxDataImportError for incorrect item ID", (done) => { getQuestionFromItem("any") .then((response) => { + expect(response.error).to.be.equal(true); done(); }) .catch((e) => { - expect(e.name).to.equal("PDFDataImportError"); + expect(e.name).to.equal("DocxDataImportError"); expect(e.message).to.equal("Invalid Response for Question ID :: any"); done(); }); }); it("[Integration test] should return Question Object for correct item ID", (done) => { - getQuestionFromItem("do_1132132526040596481722") + getQuestionFromItem("do_1134178438125649921264") .then((response) => { expect(response).to.not.be.undefined; done(); @@ -99,8 +113,7 @@ describe("Print Service", () => { }); it("[Integration test] should getData for correct Hierarchy ID", (done) => { - dataImporter - .getData("do_11326731857693900818") + getData("do_11341790341271552011559") .then((response) => { expect(response).to.not.be.undefined; expect(response).to.have.property("paperData"); @@ -117,26 +130,15 @@ describe("Print Service", () => { }); }); - xit("[Integration test] should return a PDF for correct Hierarchy ID", (done) => { - pdf.buildPDFWithCallback( - "do_11326731857693900818", - (base64PDF, error, errorMsg) => { - expect(error).to.be.false; - expect(errorMsg).to.equal(""); - done(); - } - ); - }); - it("[Integration test] should return a an error for incorrect Hierarchy ID", (done) => { - pdf.buildPDFWithCallback("any", (base64PDF, error, errorMsg) => { + docx.buildDOCXWithCallback("any", (base64, error, errorMsg) => { expect(error).to.be.true; expect(errorMsg).to.equal("Invalid ID"); done(); }); }); - it("[Integration test] should return and error for incorrect Hierarchy ID", (done) => { + it("[Integration test] should return and error for incorrect Item ID", (done) => { getQuestionFromItem("do_1132132526040596481722") .then((response) => { expect(response).to.not.be.undefined; @@ -147,6 +149,73 @@ describe("Print Service", () => { }); }); + it("[Integration test] should return Question Object for correct questions set ID for docx1.0", (done) => { + getQuestionSet("do_113431918093377536172") + .then((response) => { + expect(response).to.not.be.undefined; + done(); + }) + .catch((e) => { + done(e); + }); + }); + it("[Integration test] docx1.0 should return and error for incorrect Hierarchy ID", (done) => { + getQuestionSet("do_11341847729268326411897") + .then((response) => { + expect(response).to.not.be.undefined; + done(); + }) + .catch((e) => { + expect(e.name).to.equal("DocxDataImportError"); + expect(e.message).to.equal("Invalid Response for Itemset ID :: any"); + done(); + }); + }); + + it("[Integration test] should return Question Object for correct question ID docx1.0", (done) => { + getQuestionForSet("do_113431952169918464189") + .then((response) => { + expect(response).to.not.be.undefined; + done(); + }) + .catch((e) => { + done(e); + }); + }); + it("[Integration test] docx1.0 should return and error for incorrect question ID", (done) => { + getQuestionForSet("test") + .then((response) => { + expect(response.error).to.be.equal(true); + done(); + }) + .catch((e) => { + expect(e.name).to.equal("DocxDataImportError"); + done(); + }); + }); + + it("[Integration test] docx1.0 should getQuestionSet for correct Hierarchy ID", (done) => { + getQuestionSet("do_113431918093377536172") + .then((response) => { + expect(response).to.not.be.undefined; + expect(response).to.have.property("paperData"); + expect(response).to.have.property("sectionData"); + expect(response.sectionData).to.be.an("Array"); + expect(response.sectionData[0].questions).to.be.an("Array"); + done(); + }) + .catch((e) => { + done(e); + }); + }); + + it("[Integration test] should return a an error for incorrect Hierarchy ID", (done) => { + docx1.buildDOCX_1_WithCallback("any", (base64, error, errorMsg) => { + expect(error).to.be.true; + expect(errorMsg).to.equal(""); + done(); + }); + }); it("Should parse table", (done) => { const table = `<p>Match the following:</p><figure class="table"><table><tbody><tr><td><strong>Column 1</strong></td><td><strong>Column 2</strong></td></tr><tr><td>1</td><td>1</td></tr></tbody></table></figure>`; $ = cheerio.load(table); @@ -157,8 +226,6 @@ describe("Print Service", () => { columns.map((row) => row[colIndex]) ); const heading = $("p").text(); - - console.log(heading); done(); }); });