Commit 491ad6c8 authored by Christopher Mark Fullarton's avatar Christopher Mark Fullarton
Browse files

Adds performance improvements for attendees. Adds more config switches to...

Adds performance improvements for attendees. Adds more config switches to control the visibility of public and active quizzes
parent 6e725ace
...@@ -255,6 +255,14 @@ class QuizDAO extends AbstractDAO<Array<IQuizEntity>> { ...@@ -255,6 +255,14 @@ class QuizDAO extends AbstractDAO<Array<IQuizEntity>> {
return this.storage.find(val => val.id.equals(id)); return this.storage.find(val => val.id.equals(id));
} }
public getRenameAsToken(name: string): string {
let token;
do {
token = generateToken(name, new Date().getTime()).substr(0, 10);
} while (this.getQuizByName(token));
return token;
}
private checkABCDOrdering(quizname: string): boolean { private checkABCDOrdering(quizname: string): boolean {
let ordered = true; let ordered = true;
if (!quizname || quizname.length < 2 || quizname.charAt(0) !== 'a') { if (!quizname || quizname.length < 2 || quizname.charAt(0) !== 'a') {
......
import { ObjectId } from 'bson'; import { ObjectId } from 'bson';
import * as WebSocket from 'ws';
import CasDAO from '../../db/CasDAO'; import CasDAO from '../../db/CasDAO';
import DbDAO from '../../db/DbDAO'; import DbDAO from '../../db/DbDAO';
import QuizDAO from '../../db/quiz/QuizDAO'; import QuizDAO from '../../db/quiz/QuizDAO';
...@@ -12,16 +11,6 @@ import { ICasData } from '../../interfaces/users/ICasData'; ...@@ -12,16 +11,6 @@ import { ICasData } from '../../interfaces/users/ICasData';
import { AbstractEntity } from '../AbstractEntity'; import { AbstractEntity } from '../AbstractEntity';
export class MemberEntity extends AbstractEntity implements IMemberEntity { export class MemberEntity extends AbstractEntity implements IMemberEntity {
private _webSocket: WebSocket;
get webSocket(): WebSocket {
return this._webSocket;
}
set webSocket(value: WebSocket) {
this._webSocket = value;
}
private _currentQuizName: string; private _currentQuizName: string;
get currentQuizName(): string { get currentQuizName(): string {
......
...@@ -78,7 +78,7 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity { ...@@ -78,7 +78,7 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity {
} }
set state(value: QuizState) { set state(value: QuizState) {
if (this._state === QuizState.Inactive && value !== QuizState.Inactive) { if (this._state !== value && value !== QuizState.Inactive) {
clearInterval(this._dropEmptyQuizSettings.intervalInstance); clearInterval(this._dropEmptyQuizSettings.intervalInstance);
this._dropEmptyQuizSettings.intervalInstance = setInterval(() => { this._dropEmptyQuizSettings.intervalInstance = setInterval(() => {
this.checkExistingConnection(); this.checkExistingConnection();
...@@ -166,7 +166,7 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity { ...@@ -166,7 +166,7 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity {
this._questionList = (quiz.questionList || []).map(val => getQuestionForType(val.TYPE, val)); this._questionList = (quiz.questionList || []).map(val => getQuestionForType(val.TYPE, val));
this._sessionConfig = quiz.sessionConfig ? new SessionConfigurationEntity(quiz.sessionConfig) : null; this._sessionConfig = quiz.sessionConfig ? new SessionConfigurationEntity(quiz.sessionConfig) : null;
this._expiry = quiz.expiry; this._expiry = quiz.expiry;
this._state = quiz.state || QuizState.Inactive; this.state = quiz.state || QuizState.Inactive;
this._currentStartTimestamp = quiz.currentStartTimestamp || -1; this._currentStartTimestamp = quiz.currentStartTimestamp || -1;
this._currentQuestionIndex = typeof quiz.currentQuestionIndex !== 'undefined' ? quiz.currentQuestionIndex : -1; this._currentQuestionIndex = typeof quiz.currentQuestionIndex !== 'undefined' ? quiz.currentQuestionIndex : -1;
this._privateKey = quiz.privateKey; this._privateKey = quiz.privateKey;
...@@ -174,10 +174,6 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity { ...@@ -174,10 +174,6 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity {
this._visibility = quiz.visibility; this._visibility = quiz.visibility;
this._description = quiz.description; this._description = quiz.description;
this._exchangeName = encodeURI(`quiz_${quiz.name}`); this._exchangeName = encodeURI(`quiz_${quiz.name}`);
this._dropEmptyQuizSettings.intervalInstance = setInterval(() => {
this.checkExistingConnection();
}, this._dropEmptyQuizSettings.interval);
} }
public setInactive(): void { public setInactive(): void {
...@@ -216,7 +212,7 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity { ...@@ -216,7 +212,7 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity {
status: StatusProtocol.Success, status: StatusProtocol.Success,
step: MessageProtocol.Closed, step: MessageProtocol.Closed,
}))); })));
AMQPConnector.channel.deleteExchange(this._exchangeName); AMQPConnector.channel.deleteExchange(this._exchangeName).catch(() => {});
} }
public reset(): void { public reset(): void {
...@@ -408,15 +404,6 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity { ...@@ -408,15 +404,6 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity {
this._dropEmptyQuizSettings.isEmpty = true; this._dropEmptyQuizSettings.isEmpty = true;
}); });
response.on('error', () => {
if (this._dropEmptyQuizSettings.isEmpty) {
this.setInactive();
return;
}
this._dropEmptyQuizSettings.isEmpty = true;
});
}); });
} }
} }
import { ObjectId } from 'bson'; import { ObjectId } from 'bson';
import * as WebSocket from 'ws';
import { IQuizResponse } from '../../quizzes/IQuizResponse'; import { IQuizResponse } from '../../quizzes/IQuizResponse';
import { ICasData } from '../../users/ICasData'; import { ICasData } from '../../users/ICasData';
import { IMemberBase } from './IMemberBase'; import { IMemberBase } from './IMemberBase';
...@@ -8,7 +7,6 @@ import { IMemberSerialized } from './IMemberSerialized'; ...@@ -8,7 +7,6 @@ import { IMemberSerialized } from './IMemberSerialized';
export interface IMemberEntity extends IMemberBase { export interface IMemberEntity extends IMemberBase {
id?: ObjectId; id?: ObjectId;
casProfile: ICasData; casProfile: ICasData;
webSocket?: WebSocket;
serialize(): IMemberSerialized; serialize(): IMemberSerialized;
......
...@@ -180,16 +180,15 @@ export class MemberRouter extends AbstractRouter { ...@@ -180,16 +180,15 @@ export class MemberRouter extends AbstractRouter {
} }
const result: DeleteWriteOpResultObject = await activeQuiz.removeMember(nickname); const result: DeleteWriteOpResultObject = await activeQuiz.removeMember(nickname);
const response: Object = { status: result.deletedCount ? StatusProtocol.Success : StatusProtocol.Failed }; const response: Object = { status: result.deletedCount ? StatusProtocol.Success : StatusProtocol.Failed };
if (result && result.deletedCount) {
Object.assign(response, { Object.assign(response, {
step: MessageProtocol.Removed, step: MessageProtocol.Removed,
payload: {}, payload: {
}); ok: result.result.ok,
} else { deletedCount: result.deletedCount,
Object.assign(response, { },
message: result, });
});
}
return response; return response;
} }
......
...@@ -24,13 +24,16 @@ import AMQPConnector from '../../db/AMQPConnector'; ...@@ -24,13 +24,16 @@ import AMQPConnector from '../../db/AMQPConnector';
import { default as DbDAO } from '../../db/DbDAO'; import { default as DbDAO } from '../../db/DbDAO';
import MemberDAO from '../../db/MemberDAO'; import MemberDAO from '../../db/MemberDAO';
import QuizDAO from '../../db/quiz/QuizDAO'; import QuizDAO from '../../db/quiz/QuizDAO';
import UserDAO from '../../db/UserDAO';
import { AbstractAnswerEntity } from '../../entities/answer/AbstractAnswerEntity'; import { AbstractAnswerEntity } from '../../entities/answer/AbstractAnswerEntity';
import { QuizEntity } from '../../entities/quiz/QuizEntity'; import { QuizEntity } from '../../entities/quiz/QuizEntity';
import { DbCollection } from '../../enums/DbOperation'; import { DbCollection } from '../../enums/DbOperation';
import { MessageProtocol, StatusProtocol } from '../../enums/Message'; import { MessageProtocol, StatusProtocol } from '../../enums/Message';
import { QuizState } from '../../enums/QuizState'; import { QuizState } from '../../enums/QuizState';
import { QuizVisibility } from '../../enums/QuizVisibility'; import { QuizVisibility } from '../../enums/QuizVisibility';
import { UserRole } from '../../enums/UserRole';
import { ExcelWorkbook } from '../../export/ExcelWorkbook'; import { ExcelWorkbook } from '../../export/ExcelWorkbook';
import { IMessage } from '../../interfaces/communication/IMessage';
import { IQuizStatusPayload } from '../../interfaces/IQuizStatusPayload'; import { IQuizStatusPayload } from '../../interfaces/IQuizStatusPayload';
import { IQuizEntity, IQuizSerialized } from '../../interfaces/quizzes/IQuizEntity'; import { IQuizEntity, IQuizSerialized } from '../../interfaces/quizzes/IQuizEntity';
import { asyncForEach } from '../../lib/async-for-each'; import { asyncForEach } from '../../lib/async-for-each';
...@@ -48,7 +51,7 @@ export class QuizRouter extends AbstractRouter { ...@@ -48,7 +51,7 @@ export class QuizRouter extends AbstractRouter {
public getIsAvailableQuiz( public getIsAvailableQuiz(
@Params() params: { [key: string]: any }, // @Params() params: { [key: string]: any }, //
@HeaderParam('authorization', { required: false }) token: string, // @HeaderParam('authorization', { required: false }) token: string, //
): object { ): IMessage {
const quizName = params.quizName; const quizName = params.quizName;
const member = MemberDAO.getMemberByToken(token); const member = MemberDAO.getMemberByToken(token);
...@@ -90,6 +93,24 @@ export class QuizRouter extends AbstractRouter { ...@@ -90,6 +93,24 @@ export class QuizRouter extends AbstractRouter {
}; };
} }
@Get('/full-status/:quizName?')
public getFullQuizStatusData(
@Params() params: { [key: string]: any }, //
@HeaderParam('authorization', { required: false }) token: string, //
): object {
const status = this.getIsAvailableQuiz(params, token);
const quiz = this.getQuiz(params, token);
return {
status: status.status === StatusProtocol.Success && quiz.status === StatusProtocol.Success ? StatusProtocol.Success : StatusProtocol.Failed,
step: status.step === MessageProtocol.Available && quiz.step === MessageProtocol.Available ? MessageProtocol.Available
: MessageProtocol.Unavailable,
payload: {
status: status.payload,
quiz: quiz.payload,
},
};
}
@Get('/generate/demo/:languageId') @Get('/generate/demo/:languageId')
public generateDemoQuiz( public generateDemoQuiz(
@Param('languageId') languageId: string, // @Param('languageId') languageId: string, //
...@@ -268,6 +289,7 @@ export class QuizRouter extends AbstractRouter { ...@@ -268,6 +289,7 @@ export class QuizRouter extends AbstractRouter {
quiz.readingConfirmationRequested = false; quiz.readingConfirmationRequested = false;
quiz.currentStartTimestamp = currentStartTimestamp; quiz.currentStartTimestamp = currentStartTimestamp;
quiz.startNextQuestion(); quiz.startNextQuestion();
return { return {
status: StatusProtocol.Success, status: StatusProtocol.Success,
step: MessageProtocol.Start, step: MessageProtocol.Start,
...@@ -727,6 +749,56 @@ export class QuizRouter extends AbstractRouter { ...@@ -727,6 +749,56 @@ export class QuizRouter extends AbstractRouter {
return QuizDAO.getAllPublicQuizzes().filter(quiz => quiz.privateKey !== privateKey).map(quiz => quiz.serialize()); return QuizDAO.getAllPublicQuizzes().filter(quiz => quiz.privateKey !== privateKey).map(quiz => quiz.serialize());
} }
@Post('/public/init')
private async initQuizInstance(
@BodyParam('name') quizName: string,
@HeaderParam('X-Access-Token') loginToken: string,
@HeaderParam('authorization') privateKey: string,
): Promise<IMessage> {
const user = UserDAO.getUserByToken(loginToken);
if (!user || !user.userAuthorizations.includes(UserRole.CreateQuiz)) {
throw new UnauthorizedError('Unauthorized to create quiz');
}
const quiz = QuizDAO.getAllPublicQuizzes().find(q => q.name === quizName);
if (!quiz) {
throw new NotFoundError('Quiz name not found');
}
const serializedQuiz = quiz.serialize();
delete quiz.id;
serializedQuiz.name = QuizDAO.getRenameAsToken(serializedQuiz.name);
serializedQuiz.privateKey = privateKey;
serializedQuiz.state = QuizState.Active;
serializedQuiz.visibility = QuizVisibility.Account;
serializedQuiz.currentQuestionIndex = -1;
serializedQuiz.currentStartTimestamp = -1;
serializedQuiz.readingConfirmationRequested = false;
const quizValidator = new QuizModel(serializedQuiz);
const result = quizValidator.validateSync();
if (result) {
throw result;
}
await quizValidator.save();
AMQPConnector.channel.publish('global', '.*', Buffer.from(JSON.stringify({
status: StatusProtocol.Success,
step: MessageProtocol.SetActive,
payload: {
quizName: serializedQuiz.name,
},
})));
return {
status: StatusProtocol.Success,
step: MessageProtocol.Init,
payload: {
quiz: serializedQuiz,
},
};
}
@Get('/public/amount') @Get('/public/amount')
private getPublicQuizAmount(@HeaderParam('authorization') privateKey: string): number { private getPublicQuizAmount(@HeaderParam('authorization') privateKey: string): number {
return this.getPublicQuizzes(privateKey).length; return this.getPublicQuizzes(privateKey).length;
...@@ -751,7 +823,7 @@ export class QuizRouter extends AbstractRouter { ...@@ -751,7 +823,7 @@ export class QuizRouter extends AbstractRouter {
private getQuiz( private getQuiz(
@Params() params: { [key: string]: any }, // @Params() params: { [key: string]: any }, //
@HeaderParam('authorization', { required: false }) token: string, // @HeaderParam('authorization', { required: false }) token: string, //
): object { ): IMessage {
const quizName = params.quizName; const quizName = params.quizName;
const member = MemberDAO.getMemberByToken(token); const member = MemberDAO.getMemberByToken(token);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment