GitLab ist jetzt auf dem neuesten Stand! Die jüngsten Probleme wurden behoben. Wir entschuldigen uns für die dadurch verursachten Unannehmlichkeiten.

Commit 828cb5b0 authored by Christopher Mark Fullarton's avatar Christopher Mark Fullarton

Merge remote-tracking branch 'origin/master'

parents 341379c1 ab3eb3df
{"export":{"page_header":"&LQuiz-Statistik und Ranglisten generiert in einer Live-Session mit arsnova.click&R__createdAt__","page_footer":"&L&A&Carsnova.click ist Open Source Software, siehe arsnova.io&R &P/&N","summary":"Zusammenfassung","exported_at_date":"Exportiert am","session_content":"Archivierte Quizfragen (Um das Quiz zu importieren, kopiere den vollständigen Zellinhalt in eine leere .json-Datei. Importiere dann die Datei mit dem entsprechenden Icon in der Fußzeile der App.)","cas_account_id":"CAS Account-ID","cas_account_email":"CAS E-Mail","quiz_name":"Quizname","question":"Quizfrage","question_type":"Typ der Quizfrage","number_of_answers":"Anzahl der Antworten","percent_correct":"Korrekte Antworten (in %)","answer":"Antwort","attendee":"Teilnehmer/in","attendee_complete_correct":"Teilnehmer/innen mit vollständig richtig beantworteten Fragen","attendee_complete_correct_none_available":"Keine Einträge vorhanden","correct_questions_none_available":"Keine","attendee_all_entries":"Alle Teilnehmer/innen","time":"Antwortzeit (in ms)","confidence_level":"Antwortsicherheit","correct_value":"Richtiger Wert","min_range":"Mindestwert","max_range":"Maximalwert","correct_questions":"Richtig beantwortete Fragen","overall_response_time":"Kumulierte Antwortzeit (in ms)","average_response_time":"Durchschnittliche Antwortzeit (in ms)","number_attendees":"Anzahl der Teilnehmer/innen","average_number_attendees_participated":"Durchschnittliche Teilnahmen am Quiz (in %)","average_correct_answered_questions":"Durchschnittlich richtig beantwortete Fragen","average_confidence":"Durchschnittliche Antwortsicherheit (in %)","exported_at":"um","exported_at_time":"Uhr","type":{"SurveyQuestion":"Umfragen","TrueFalseSingleChoiceQuestion":"Wahr-Falsch Frage","YesNoSingleChoiceQuestion":"Ja-Nein Frage","ABCDSingleChoiceQuestion":"ABCD Single-Choice Frage","SingleChoiceQuestion":"Single-Choice Frage","RangedQuestion":"Schätzfrage","FreeTextQuestion":"Freitext Frage","MultipleChoiceQuestion":"Multiple-Choice Frage"}},"view":{"answeroptions":{"free_text_question":{"config_case_sensitive":"Groß- und Kleinschreibung beachten","config_trim_whitespaces":"Leerzeichen entfernen","config_use_keywords":"Reihenfolge der Wörter beachten","config_use_punctuation":"Interpunktion beachten"}}},"global":{"no":"Nein","yes":"Ja"}}
\ No newline at end of file
{
"export": {
"page_header": "&LQuiz-Statistik und Ranglisten generiert in einer Live-Session mit arsnova.click&R__createdAt__",
"page_footer": "&L&A&Carsnova.click ist Open Source Software, siehe arsnova.io&R &P/&N",
"summary": "Zusammenfassung",
"exported_at_date": "Exportiert am",
"session_content": "Archivierte Quizfragen (Um das Quiz zu importieren, kopiere den vollständigen Zellinhalt in eine leere .json-Datei. Importiere dann die Datei mit dem entsprechenden Icon in der Fußzeile der App.)",
"cas_account_id": "CAS Account-ID",
"cas_account_email": "CAS E-Mail",
"quiz_name": "Quizname",
"question": "Quizfrage",
"question_type": "Typ der Quizfrage",
"number_of_answers": "Anzahl der Antworten",
"percent_correct": "Korrekte Antworten (in %)",
"answer": "Antwort",
"attendee": "Teilnehmer/in",
"attendee_complete_correct": "Teilnehmer/innen mit vollständig richtig beantworteten Fragen",
"attendee_complete_correct_none_available": "Keine Einträge vorhanden",
"correct_questions_none_available": "Keine",
"attendee_all_entries": "Alle Teilnehmer/innen",
"time": "Antwortzeit (in ms)",
"confidence_level": "Antwortsicherheit",
"correct_value": "Richtiger Wert",
"min_range": "Mindestwert",
"max_range": "Maximalwert",
"correct_questions": "Richtig beantwortete Fragen",
"overall_response_time": "Kumulierte Antwortzeit (in ms)",
"average_response_time": "Durchschnittliche Antwortzeit (in ms)",
"number_attendees": "Anzahl der Teilnehmer/innen",
"average_number_attendees_participated": "Durchschnittliche Teilnahmen am Quiz (in %)",
"average_correct_answered_questions": "Durchschnittlich richtig beantwortete Fragen",
"average_confidence": "Durchschnittliche Antwortsicherheit (in %)",
"exported_at": "um",
"exported_at_time": "Uhr",
"type": {
"SurveyQuestion": "Umfrage",
"TrueFalseSingleChoiceQuestion": "Wahr-Falsch Frage",
"YesNoSingleChoiceQuestion": "Ja-Nein Frage",
"ABCDSingleChoiceQuestion": "ABCD Single-Choice Frage",
"SingleChoiceQuestion": "Single-Choice Frage",
"RangedQuestion": "Schätzfrage",
"FreeTextQuestion": "Freitext Frage",
"MultipleChoiceQuestion": "Multiple-Choice Frage"
}
},
"view": {
"answeroptions": {
"free_text_question": {
"config_case_sensitive": "Groß- und Kleinschreibung beachten",
"config_trim_whitespaces": "Leerzeichen entfernen",
"config_use_keywords": "Reihenfolge der Wörter beachten",
"config_use_punctuation": "Interpunktion beachten"
}
}
},
"global": {
"no": "Nein",
"yes": "Ja"
}
}
\ No newline at end of file
......@@ -40,7 +40,7 @@ class DumpCryptor {
let salt = null;
if (fs.existsSync(this.pathToSalt)) {
salt = JSON.parse(fs.readFileSync(this.pathToSalt));
salt = JSON.parse(fs.readFileSync(this.pathToSalt, {encoding: 'UTF-8'}));
} else {
const bytesInSalt = 128 / 8;
salt = CryptoJS.lib.WordArray.random(bytesInSalt);
......
......@@ -15,80 +15,73 @@
"dependency-check": "npx --ignore-existing madge --circular --extensions ts src"
},
"dependencies": {
"api-spec-converter": "^2.7.32",
"body-parser": "^1.18.3",
"bull": "^3.7.0",
"api-spec-converter": "^2.8.3",
"body-parser": "^1.19.0",
"bunyan": "^1.8.12",
"cas": "0.0.3",
"compression": "^1.7.4",
"connect-busboy": "0.0.2",
"cookie-parser": "^1.4.4",
"cors": "^2.8.5",
"crypto-js": "^3.1.9-1",
"cssstyle": "^1.2.2",
"excel4node": "^1.7.1",
"express": "^4.16.4",
"excel4node": "^1.7.2",
"express": "^4.17.1",
"express-ws": "^4.0.0",
"file-type": "^10.10.0",
"file-type": "^12.0.0",
"gitlab": "^4.5.1",
"i18n": "^0.8.3",
"jsonwebtoken": "^8.5.1",
"lowdb": "^1.0.0",
"mathjax-node": "^2.1.1",
"messageformat": "^2.1.0",
"mime": "^2.4.2",
"mime-types": "^2.1.22",
"messageformat": "^2.2.1",
"mime": "^2.4.4",
"mime-types": "^2.1.24",
"minimist": "^1.2.0",
"mongoose": "^5.4.22",
"morgan": "^1.9.1",
"multer": "^1.4.1",
"node-sass-middleware": "0.11.0",
"nodemailer": "^5.1.1",
"nodemailer": "^6.2.1",
"reflect-metadata": "^0.1.13",
"request": "^2.88.0",
"request-promise-native": "^1.0.7",
"routing-controllers": "^0.7.7",
"routing-controllers-openapi": "^1.6.0",
"source-map-support": "^0.5.11",
"swagger-ui-express": "^4.0.2",
"typegoose": "^5.6.0",
"url-join": "^4.0.0",
"ws": "^6.2.1",
"routing-controllers-openapi": "^1.7.0",
"source-map-support": "^0.5.12",
"swagger-ui-express": "^4.0.6",
"typegoose": "^5.7.2",
"ws": "^7.0.1",
"xml2js": "^0.4.19"
},
"devDependencies": {
"@types/body-parser": "1.17.0",
"@types/bull": "^3.5.11",
"@types/bull": "^3.5.14",
"@types/bunyan": "^1.8.6",
"@types/busboy": "^0.2.3",
"@types/chai": "^4.1.7",
"@types/chai-http": "3.0.5",
"@types/chai-http": "4.2.0",
"@types/compression": "^0.0.36",
"@types/cors": "^2.8.4",
"@types/cors": "^2.8.5",
"@types/crypto-js": "^3.1.43",
"@types/express": "^4.16.1",
"@types/express": "^4.17.0",
"@types/file-type": "^10.9.1",
"@types/i18n": "^0.8.5",
"@types/jsonwebtoken": "^8.3.2",
"@types/lowdb": "^1.0.7",
"@types/minimist": "^1.2.0",
"@types/mocha": "^5.2.6",
"@types/mocha": "^5.2.7",
"@types/mongoose": "^5.3.24",
"@types/morgan": "^1.7.35",
"@types/node": "^10.14.4",
"@types/node": "^12.0.10",
"@types/request": "^2.48.1",
"@types/request-promise-native": "^1.0.15",
"@types/request-promise-native": "^1.0.16",
"@types/websocket": "0.0.40",
"@types/ws": "^6.0.1",
"@types/xml2js": "^0.4.4",
"chai": "^4.2.0",
"chai-http": "^4.2.1",
"mocha": "^5.2.0",
"chai-http": "^4.3.0",
"mocha": "^6.1.4",
"mocha-typescript": "^1.1.17",
"nyc": "^13.3.0",
"ts-loader": "^5.3.3",
"ts-node": "^8.0.3",
"tslint": "^5.15.0",
"typescript": "^3.4.2"
"nyc": "^14.1.1",
"ts-loader": "^6.0.4",
"ts-node": "^8.3.0",
"tslint": "^5.18.0",
"typescript": "^3.5.2"
}
}
......@@ -63,11 +63,9 @@ class MongoDbConnector {
this._mongoURL += `${mongoHost}:${mongoPort}/${mongoDatabase}`;
const mongoURLOptions = [];
if (process.env.MONGODB_REPLICA_NAME) {
mongoURLOptions.push(`replicaSet=${mongoReplSet}`);
}
mongoURLOptions.push(`replicaSet=${mongoReplSet}`);
if (process.env.MONGODB_AUTH_SOURCE) {
if (Boolean(process.env.MONGODB_AUTH_SOURCE)) {
if (process.env.MONGODB_AUTH_SOURCE === 'true') {
mongoURLOptions.push(`authSource=${process.env.MONGODB_AUTH_SOURCE}`);
}
} else {
......
......@@ -94,6 +94,14 @@ class UserDAO extends AbstractDAO<{ [key: string]: IUserEntity }> {
return this.storage[name];
}
public getUserByTokenHash(tokenHash: string): IUserEntity {
if (this.isEmptyVars(tokenHash)) {
return null;
}
return Object.values(this.storage).find(user => user.tokenHash === tokenHash);
}
public getUserById(id: ObjectId): IUserEntity {
return Object.values(this.storage).find(val => val.id.equals(id));
}
......
......@@ -6,6 +6,16 @@ import { AuthService } from '../services/AuthService';
import { AbstractEntity } from './AbstractEntity';
export class UserEntity extends AbstractEntity implements IUserEntity {
private _tokenHash: string;
get tokenHash(): string {
return this._tokenHash;
}
set tokenHash(value: string) {
this._tokenHash = value;
}
private _token: string;
get token(): string {
......@@ -73,6 +83,7 @@ export class UserEntity extends AbstractEntity implements IUserEntity {
this._name = data.name;
this._privateKey = data.privateKey;
this._passwordHash = data.passwordHash;
this._tokenHash = data.tokenHash;
this._gitlabToken = data.gitlabToken;
this._token = data.token;
this._userAuthorizations = data.userAuthorizations.map(val => UserRole[val]);
......@@ -94,6 +105,7 @@ export class UserEntity extends AbstractEntity implements IUserEntity {
token: this.token,
name: this.name,
passwordHash: this.passwordHash,
tokenHash: this.tokenHash,
privateKey: this.privateKey,
gitlabToken: this.gitlabToken,
userAuthorizations: this.userAuthorizations,
......
......@@ -183,7 +183,7 @@ export class MemberEntity extends AbstractEntity implements IMemberEntity {
responses[i] = {
value: [],
responseTime: -1,
confidence: 0,
confidence: -1,
readingConfirmation: false,
};
}
......
import * as xlsx from 'excel4node';
import * as MessageFormat from 'messageformat';
import MemberDAO from '../db/MemberDAO';
import { AbstractQuestionEntity } from '../entities/question/AbstractQuestionEntity';
import { IMemberEntity } from '../interfaces/entities/Member/IMemberEntity';
import { ILeaderBoardItemBase } from '../interfaces/leaderboard/ILeaderBoardItemBase';
import { IQuizEntity } from '../interfaces/quizzes/IQuizEntity';
......@@ -78,7 +77,7 @@ export abstract class ExcelWorksheet {
});
} else {
this._responsesWithConfidenceValue = MemberDAO.getMembersOfQuiz(this._quiz.name).filter(nickname => {
return nickname.responses.filter(responseItem => responseItem.confidence > -1);
return nickname.responses.some(responseItem => responseItem.confidence > -1);
});
}
if (this._responsesWithConfidenceValue.length > 0) {
......@@ -100,27 +99,8 @@ export abstract class ExcelWorksheet {
protected getLeaderboardData(questionIndex: number): Array<ILeaderBoardItemBase> {
const leaderBoard = new Leaderboard();
const correctResponses: any = {};
const question: AbstractQuestionEntity = this.quiz.questionList[questionIndex];
MemberDAO.getMembersOfQuiz(this._quiz.name).forEach(attendee => {
if (leaderBoard.isCorrectResponse(attendee.responses[questionIndex], question) === 1) {
if (!correctResponses[attendee.name]) {
correctResponses[attendee.name] = {
responseTime: 0,
correctQuestions: [],
confidenceValue: 0,
};
}
correctResponses[attendee.name].responseTime += <number>attendee.responses[questionIndex].responseTime;
correctResponses[attendee.name].correctQuestions.push(questionIndex);
correctResponses[attendee.name].confidenceValue += <number>attendee.responses[questionIndex].confidence;
} else {
delete correctResponses[attendee.name];
}
});
return leaderBoard.objectToArray(correctResponses);
const { correctResponses } = leaderBoard.buildLeaderboard(this.quiz);
return leaderBoard.sortBy(correctResponses, 'score');
}
private prefixNumberWithZero(num: number): string {
......
......@@ -44,7 +44,7 @@ export class FreeTextExcelWorksheet extends ExcelWorksheet implements IExcelWork
this.ws.row(1).setHeight(20);
this.ws.column(1).setWidth(this.responsesWithConfidenceValue.length > 0 ? 40 : 30);
this.ws.column(2).setWidth(30);
this.ws.column(3).setWidth(35);
this.ws.column(3).setWidth(45);
this.ws.column(4).setWidth(35);
this.ws.cell(1, 1, 1, columnsToFormat).style(defaultStyles.quizNameRowStyle);
......@@ -156,7 +156,9 @@ export class FreeTextExcelWorksheet extends ExcelWorksheet implements IExcelWork
${this.mf(answerOption.configTrimWhitespaces ? 'global.yes' : 'global.no')}`);
this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':');
const correctResponsesPercentage: number = this.leaderBoardData.length / MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions)
.filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length
/ MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage)));
this.ws.cell(7, 3).string(`
......
......@@ -150,8 +150,12 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc
this.ws.cell(2, 1).string(this.mf('export.question'));
this.ws.cell(6, 1).string(this.mf('export.number_of_answers') + ':');
this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':');
const correctResponsesPercentage: number = this.leaderBoardData.length / MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions)
.filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length
/ MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage)));
if (this.responsesWithConfidenceValue.length > 0) {
this.ws.cell(8, 1).string(this.mf('export.average_confidence') + ':');
let confidenceSummary = 0;
......@@ -160,22 +164,30 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc
});
this.ws.cell(8, 2).number(Math.round(confidenceSummary / this.responsesWithConfidenceValue.length));
}
this.ws.cell(4, 1).string(this._question.questionText.replace(/[#]*[*]*/g, ''));
for (let j = 0; j < answerList.length; j++) {
this.ws.cell(2, (j + 2)).string(this.mf('export.answer') + ' ' + (j + 1));
this.ws.cell(4, (j + 2)).string(answerList[j].answerText);
this.ws.cell(6, (j + 2)).number(calculateNumberOfAnswers(this.quiz, this._questionIndex, j));
}
let nextColumnIndex = 1;
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.attendee'));
if (this._isCasRequired) {
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.cas_account_id'));
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.cas_account_email'));
}
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.answer'));
if (this.responsesWithConfidenceValue.length > 0) {
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.confidence_level'));
}
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.time'));
let nextStartRow = 10;
......
......@@ -211,7 +211,9 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh
this.ws.cell(6, 4).number(numberOfInputValuesPerGroup.maxRange);
this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':');
const correctResponsesPercentage: number = this.leaderBoardData.length / MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions)
.filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length
/ MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage)));
if (this.responsesWithConfidenceValue.length > 0) {
......
......@@ -167,7 +167,9 @@ export class SingleChoiceExcelWorksheet extends ExcelWorksheet implements IExcel
this.ws.cell(6, 1).string(this.mf('export.number_of_answers') + ':');
this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':');
const correctResponsesPercentage: number = this.leaderBoardData.length / MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions)
.filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length
/ MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage)));
if (this.responsesWithConfidenceValue.length > 0) {
......
......@@ -3,8 +3,6 @@ import MemberDAO from '../db/MemberDAO';
import { QuestionType } from '../enums/QuestionType';
import { IMemberEntity } from '../interfaces/entities/Member/IMemberEntity';
import { IExcelWorksheet } from '../interfaces/iExcel';
import { ILeaderBoardItemBase } from '../interfaces/leaderboard/ILeaderBoardItemBase';
import { Leaderboard } from '../lib/leaderboard/leaderboard';
import { staticStatistics } from '../statistics';
import { ExcelWorksheet } from './ExcelWorksheet';
......@@ -73,43 +71,52 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks
this.ws.cell(1, 1, 2, 1).style({
alignment: {
indent: 7,
indent: 5,
},
});
this.ws.cell(4, 1, 8, this.columnsToFormat).style(defaultStyles.statisticsRowStyle);
this.ws.cell(4, 3, 8, 3).style({
let currentRowIndex = 7;
if (this.quiz.sessionConfig.confidenceSliderEnabled) {
currentRowIndex++;
}
this.ws.cell(4, 1, currentRowIndex, this.columnsToFormat).style(defaultStyles.statisticsRowStyle);
this.ws.cell(4, 3, currentRowIndex, 3).style({
alignment: {
horizontal: 'left',
},
});
this.ws.cell(8, 3).style({
if (this.quiz.sessionConfig.confidenceSliderEnabled) {
}
this.ws.cell(this.quiz.sessionConfig.confidenceSliderEnabled ? currentRowIndex - 1 : currentRowIndex, 3, currentRowIndex, 3).style({
numberFormat: '#,##0',
});
this.ws.cell(10, 1, 11, this.columnsToFormat).style(defaultStyles.attendeeHeaderGroupRowStyle);
this.ws.cell(12, 1, 12, this.columnsToFormat).style(defaultStyles.attendeeHeaderRowStyle);
this.ws.cell(12, 1).style({
currentRowIndex += 2;
this.ws.cell(currentRowIndex, 1, currentRowIndex, this.columnsToFormat).style(defaultStyles.attendeeHeaderGroupRowStyle);
this.ws.cell(++currentRowIndex, 1, currentRowIndex, this.columnsToFormat).style(defaultStyles.attendeeHeaderRowStyle);
this.ws.cell(currentRowIndex, 1).style({
alignment: {
horizontal: 'left',
},
});
this.ws.row(12).filter({
firstRow: 12,
this.ws.row(currentRowIndex).filter({
firstRow: currentRowIndex,
firstColumn: 1,
lastRow: 12,
lastColumn: this.columnsToFormat,
lastRow: currentRowIndex,
lastColumn: this.columnsToFormat - 1,
});
let nextStartRow = 18;
let dataWithoutCompleteCorrectQuestions = 0;
this.leaderBoardData.forEach((leaderboardItem, indexInList) => {
let hasNotAllQuestionsCorrect = false;
this.quiz.questionList.forEach((item, index) => {
if ([
QuestionType.SurveyQuestion, QuestionType.ABCDSingleChoiceQuestion,
].includes(item.TYPE) && leaderboardItem.correctQuestions.indexOf((index)) === -1) {
if (![
QuestionType.SurveyQuestion, QuestionType.ABCDSingleChoiceQuestion,
].includes(item.TYPE) && leaderboardItem.correctQuestions.indexOf((index)) === -1) {
hasNotAllQuestionsCorrect = true;
}
});
......@@ -117,57 +124,59 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks
dataWithoutCompleteCorrectQuestions++;
return;
}
let nextColumnIndex = 3;
nextStartRow++;
const targetRow = indexInList + 13;
let nextColumnIndex = 2;
currentRowIndex++;
if (this.quiz.sessionConfig.confidenceSliderEnabled) {
this.ws.cell(targetRow, nextColumnIndex++).style({
this.ws.cell(currentRowIndex, nextColumnIndex++).style({
alignment: {
horizontal: 'center',
},
});
}
this.ws.cell(targetRow, nextColumnIndex++).style({
this.ws.cell(currentRowIndex, nextColumnIndex++).style({
alignment: {
horizontal: 'center',
},
numberFormat: '#,##0;',
});
this.ws.cell(targetRow, nextColumnIndex).style({
this.ws.cell(currentRowIndex, nextColumnIndex).style({
alignment: {
horizontal: 'center',
},
numberFormat: '#,##0;',
});
});
if (nextStartRow === 18) {
this.ws.cell(13, 1, 13, this.columnsToFormat, true).style(Object.assign({}, defaultStyles.attendeeEntryRowStyle, {
if (dataWithoutCompleteCorrectQuestions === this.leaderBoardData.length) {
this.ws.cell(++currentRowIndex, 1, currentRowIndex, this.columnsToFormat, true).style(Object.assign({}, defaultStyles.attendeeEntryRowStyle, {
alignment: {
horizontal: 'center',
},
}));
nextStartRow++;
} else {
this.ws.cell(13, 1, (this.leaderBoardData.length + 12 - dataWithoutCompleteCorrectQuestions), this.columnsToFormat)
this.ws.cell(currentRowIndex, 1, (this.leaderBoardData.length + currentRowIndex - 1 - dataWithoutCompleteCorrectQuestions),
this.columnsToFormat)
.style(defaultStyles.attendeeEntryRowStyle);
}
currentRowIndex += 6;
this.ws.cell(nextStartRow++, 1, nextStartRow++, this.columnsToFormat).style(defaultStyles.attendeeHeaderGroupRowStyle);
this.ws.cell(currentRowIndex, 1, currentRowIndex, this.columnsToFormat).style(defaultStyles.attendeeHeaderGroupRowStyle);
currentRowIndex++;
this.ws.cell(nextStartRow, 1, nextStartRow, this.columnsToFormat).style(defaultStyles.attendeeHeaderRowStyle);
this.ws.cell(nextStartRow, 1).style({
this.ws.cell(currentRowIndex, 1, currentRowIndex, this.columnsToFormat).style(defaultStyles.attendeeHeaderRowStyle);
this.ws.cell(currentRowIndex, 1).style({
alignment: {
horizontal: 'left',
},
});
nextStartRow++;
currentRowIndex++;
this.ws.cell(nextStartRow, 1, (this.leaderBoardData.length + (nextStartRow - 1)), this.columnsToFormat)
this.ws.cell(currentRowIndex, 1, (this.leaderBoardData.length + (currentRowIndex - 1)), this.columnsToFormat)
.style(defaultStyles.attendeeEntryRowStyle);
this.leaderBoardData.forEach((leaderboardItem, indexInList) => {
let nextColumnIndex = 3;
const targetRow = indexInList + nextStartRow;
const targetRow = indexInList + currentRowIndex;
if (this.quiz.sessionConfig.confidenceSliderEnabled) {
this.ws.cell(targetRow, nextColumnIndex++).style({
alignment: {
......@@ -252,16 +261,15 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks
let nextColumnIndex = 1;
this.ws.cell(currentRowIndex, nextColumnIndex).string(this.mf('export.attendee_complete_correct'));
currentRowIndex += 2;
currentRowIndex += 1;
this.ws.cell(currentRowIndex, nextColumnIndex++).string(this.mf('export.attendee'));
if (this._isCasRequired) {
this.ws.cell(currentRowIndex, nextColumnIndex++).string(this.mf('export.cas_account_id'));
this.ws.cell(currentRowIndex, nextColumnIndex++).string(this.mf('export.cas_account_email'));
}
this.ws.cell(currentRowIndex, nextColumnIndex++).string(this.mf('export.correct_questions'));
if (this.quiz.sessionConfig.confidenceSliderEnabled) {
this.ws.cell(currentRowIndex, nextColumnIndex++).string(this.mf('export.average_confidence'));
}
......@@ -287,13 +295,9 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks
this.ws.cell(targetRow, nextColumnIndex++).string(profile.username[0]);
this.ws.cell(targetRow, nextColumnIndex++).string(profile.mail[0]);
}
const correctQuestionNumbers = this.leaderBoardData[indexInList].correctQuestions.map((item) => item + 1);
if (this.quiz.sessionConfig.confidenceSliderEnabled) {
this.ws.cell(targetRow, nextColumnIndex++).string(correctQuestionNumbers.join(', '));
if (this.responsesWithConfidenceValue.length > 0) {
this.ws.cell(targetRow, nextColumnIndex++).number(Math.round(leaderboardItem.confidenceValue));
}
if (this.responsesWithConfidenceValue.length > 0) {
this.ws.cell(targetRow, nextColumnIndex++).number(Math.round(leaderboardItem.confidenceValue));
}
this.ws.cell(targetRow, nextColumnIndex++).number(Math.round(leaderboardItem.responseTime));
......@@ -307,7 +311,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks