Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
arsnova-click-v2-backend
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
ARSnova
arsnova-click-v2-backend
Commits
25a14245
Commit
25a14245
authored
Feb 17, 2019
by
Christopher Mark Fullarton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixes leaderboard calculation
parent
bd60944e
Changes
20
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
110 additions
and
78 deletions
+110
-78
src/db/MemberDAO.ts
src/db/MemberDAO.ts
+2
-1
src/db/quiz/QuizDAO.ts
src/db/quiz/QuizDAO.ts
+15
-1
src/entities/member/MemberGroupEntity.ts
src/entities/member/MemberGroupEntity.ts
+5
-8
src/entities/quiz/QuizEntity.ts
src/entities/quiz/QuizEntity.ts
+2
-2
src/export/ExcelWorksheet.ts
src/export/ExcelWorksheet.ts
+4
-3
src/export/FreeTextExcelWorksheet.ts
src/export/FreeTextExcelWorksheet.ts
+3
-2
src/export/MultipleChoiceExcelWorksheet.ts
src/export/MultipleChoiceExcelWorksheet.ts
+7
-5
src/export/RangedExcelWorksheet.ts
src/export/RangedExcelWorksheet.ts
+5
-4
src/export/SingleChoiceExcelWorksheet.ts
src/export/SingleChoiceExcelWorksheet.ts
+5
-4
src/export/SummaryExcelWorksheet.ts
src/export/SummaryExcelWorksheet.ts
+6
-5
src/export/SurveyExcelWorksheet.ts
src/export/SurveyExcelWorksheet.ts
+3
-2
src/export/lib/excel_function_library.ts
src/export/lib/excel_function_library.ts
+9
-7
src/interfaces/entities/Member/IMemberEntity.ts
src/interfaces/entities/Member/IMemberEntity.ts
+2
-0
src/interfaces/users/IMemberGroupEntity.ts
src/interfaces/users/IMemberGroupEntity.ts
+1
-2
src/interfaces/users/IMemberGroupSerialized.ts
src/interfaces/users/IMemberGroupSerialized.ts
+1
-2
src/lib/leaderboard/leaderboard.ts
src/lib/leaderboard/leaderboard.ts
+25
-3
src/routers/rest/MemberRouter.ts
src/routers/rest/MemberRouter.ts
+1
-1
src/routers/rest/QuizRouter.ts
src/routers/rest/QuizRouter.ts
+3
-3
src/routers/websocket/WebSocketRouter.ts
src/routers/websocket/WebSocketRouter.ts
+2
-9
src/tests/export/export.test.ts
src/tests/export/export.test.ts
+9
-14
No files found.
src/db/MemberDAO.ts
View file @
25a14245
import
{
ObjectId
}
from
'
bson
'
;
import
{
MemberEntity
}
from
'
../entities/member/MemberEntity
'
;
import
{
DbCollection
,
DbEvent
}
from
'
../enums/DbOperation
'
;
import
{
IMemberEntity
}
from
'
../interfaces/entities/Member/IMemberEntity
'
;
import
{
IMemberSerialized
}
from
'
../interfaces/entities/Member/IMemberSerialized
'
;
import
{
AbstractDAO
}
from
'
./AbstractDAO
'
;
import
DbDAO
from
'
./DbDAO
'
;
...
...
@@ -66,7 +67,7 @@ class MemberDAO extends AbstractDAO<Array<MemberEntity>> {
}
}
public
getMembersOfQuiz
(
quizName
:
string
):
Array
<
MemberEntity
>
{
public
getMembersOfQuiz
(
quizName
:
string
):
Array
<
I
MemberEntity
>
{
return
this
.
storage
.
filter
(
val
=>
!!
val
.
currentQuizName
.
match
(
new
RegExp
(
`^
${
quizName
}
$`
,
'
i
'
)));
}
...
...
src/db/quiz/QuizDAO.ts
View file @
25a14245
...
...
@@ -143,11 +143,25 @@ class QuizDAO extends AbstractDAO<Array<IQuizEntity>> {
this
.
updateEmitter
.
emit
(
DbEvent
.
Change
,
this
.
getJoinableQuizzes
());
}
public
a
ddQuiz
(
doc
:
IQuizSerialized
):
IQuizEntity
{
public
a
sync
addQuiz
(
doc
:
IQuizSerialized
):
Promise
<
IQuizEntity
>
{
if
(
this
.
getQuizByName
(
doc
.
name
))
{
throw
new
Error
(
`Duplicate quiz insertion:
${
doc
.
name
}
`
);
}
await
DbDAO
.
readMany
(
DbCollection
.
Members
,
{
currentQuizName
:
doc
.
name
}).
forEach
(
memberDoc
=>
{
if
(
!
Array
.
isArray
(
doc
.
memberGroups
))
{
doc
.
memberGroups
=
[];
}
if
(
!
doc
.
memberGroups
.
find
(
memberGroup
=>
memberGroup
.
name
===
memberDoc
.
groupName
))
{
doc
.
memberGroups
.
push
(
new
MemberGroupEntity
({
name
:
memberDoc
.
groupName
}));
}
if
(
doc
.
memberGroups
.
find
(
memberGroup
=>
memberGroup
.
name
===
memberDoc
.
groupName
).
members
.
includes
(
memberDoc
.
name
))
{
return
;
}
doc
.
memberGroups
.
find
(
memberGroup
=>
memberGroup
.
name
===
memberDoc
.
groupName
).
members
.
push
(
memberDoc
.
name
);
});
const
entity
=
new
QuizEntity
(
doc
);
this
.
storage
.
push
(
entity
);
return
entity
;
...
...
src/entities/member/MemberGroupEntity.ts
View file @
25a14245
import
MemberDAO
from
'
../../db/MemberDAO
'
;
import
{
IMemberEntity
}
from
'
../../interfaces/entities/Member/IMemberEntity
'
;
import
{
IMemberSerialized
}
from
'
../../interfaces/entities/Member/IMemberSerialized
'
;
import
{
IMemberGroupEntity
}
from
'
../../interfaces/users/IMemberGroupEntity
'
;
import
{
IMemberGroupSerialized
}
from
'
../../interfaces/users/IMemberGroupSerialized
'
;
import
{
AbstractEntity
}
from
'
../AbstractEntity
'
;
export
class
MemberGroupEntity
extends
AbstractEntity
implements
IMemberGroupEntity
{
private
_members
:
Array
<
IMemberEntity
>
;
private
_members
:
Array
<
string
>
;
get
members
():
Array
<
IMemberEntity
>
{
get
members
():
Array
<
string
>
{
return
this
.
_members
;
}
set
members
(
value
:
Array
<
IMemberEntity
>
)
{
set
members
(
value
:
Array
<
string
>
)
{
this
.
_members
=
value
;
}
...
...
@@ -26,13 +23,13 @@ export class MemberGroupEntity extends AbstractEntity implements IMemberGroupEnt
super
();
this
.
_name
=
data
.
name
;
this
.
_members
=
(
data
.
members
||
[]).
map
(
val
=>
MemberDAO
.
getMemberByName
(
val
.
name
))
;
this
.
_members
=
data
.
members
||
[]
;
}
public
serialize
():
IMemberGroupSerialized
{
return
{
name
:
this
.
name
,
members
:
this
.
members
.
map
(
val
=>
val
?
val
.
serialize
()
:
{}
as
IMemberSerialized
)
,
members
:
this
.
members
,
};
}
}
src/entities/quiz/QuizEntity.ts
View file @
25a14245
...
...
@@ -184,7 +184,7 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity {
return
;
}
this
.
memberGroups
.
find
(
group
=>
group
.
name
===
member
.
groupName
).
members
.
push
(
member
);
this
.
memberGroups
.
find
(
group
=>
group
.
name
===
member
.
groupName
).
members
.
push
(
member
.
name
);
this
.
_socketChannel
.
forEach
(
socket
=>
SendSocketMessageService
.
sendMessage
(
socket
,
{
status
:
StatusProtocol
.
Success
,
step
:
MessageProtocol
.
Added
,
...
...
@@ -197,7 +197,7 @@ export class QuizEntity extends AbstractEntity implements IQuizEntity {
}
const
memberGroup
=
this
.
memberGroups
.
find
(
group
=>
group
.
name
===
member
.
groupName
);
memberGroup
.
members
.
splice
(
memberGroup
.
members
.
findIndex
(
quizMember
s
=>
quizMembers
.
name
===
member
.
name
),
1
);
memberGroup
.
members
.
splice
(
memberGroup
.
members
.
findIndex
(
quizMember
=>
quizMember
===
member
.
name
),
1
);
this
.
_socketChannel
.
forEach
(
socket
=>
SendSocketMessageService
.
sendMessage
(
socket
,
{
status
:
StatusProtocol
.
Success
,
...
...
src/export/ExcelWorksheet.ts
View file @
25a14245
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
'
;
...
...
@@ -72,11 +73,11 @@ export abstract class ExcelWorksheet {
this
.
_columnsToFormat
=
4
;
if
(
questionIndex
)
{
this
.
_responsesWithConfidenceValue
=
this
.
_quiz
.
memberGroups
[
0
].
members
.
filter
(
nickname
=>
{
this
.
_responsesWithConfidenceValue
=
MemberDAO
.
getMembersOfQuiz
(
this
.
_quiz
.
name
)
.
filter
(
nickname
=>
{
return
nickname
.
responses
[
questionIndex
].
confidence
>
-
1
;
});
}
else
{
this
.
_responsesWithConfidenceValue
=
this
.
_quiz
.
memberGroups
[
0
].
members
.
filter
(
nickname
=>
{
this
.
_responsesWithConfidenceValue
=
MemberDAO
.
getMembersOfQuiz
(
this
.
_quiz
.
name
)
.
filter
(
nickname
=>
{
return
nickname
.
responses
.
filter
(
responseItem
=>
responseItem
.
confidence
>
-
1
);
});
}
...
...
@@ -102,7 +103,7 @@ export abstract class ExcelWorksheet {
const
correctResponses
:
any
=
{};
const
question
:
AbstractQuestionEntity
=
this
.
quiz
.
questionList
[
questionIndex
];
this
.
quiz
.
memberGroups
[
0
].
members
.
forEach
(
attendee
=>
{
MemberDAO
.
getMembersOfQuiz
(
this
.
_quiz
.
name
)
.
forEach
(
attendee
=>
{
if
(
leaderBoard
.
isCorrectResponse
(
attendee
.
responses
[
questionIndex
],
question
)
===
1
)
{
if
(
!
correctResponses
[
attendee
.
name
])
{
correctResponses
[
attendee
.
name
]
=
{
...
...
src/export/FreeTextExcelWorksheet.ts
View file @
25a14245
import
MemberDAO
from
'
../db/MemberDAO
'
;
import
{
FreeTextAnswerEntity
}
from
'
../entities/answer/FreetextAnwerEntity
'
;
import
{
FreeTextQuestionEntity
}
from
'
../entities/question/FreeTextQuestionEntity
'
;
import
{
IMemberEntity
}
from
'
../interfaces/entities/Member/IMemberEntity
'
;
...
...
@@ -8,7 +9,7 @@ export class FreeTextExcelWorksheet extends ExcelWorksheet implements IExcelWork
private
_isCasRequired
=
this
.
quiz
.
sessionConfig
.
nicks
.
restrictToCasLogin
;
private
_question
:
FreeTextQuestionEntity
;
private
readonly
_questionIndex
:
number
;
private
allResponses
:
Array
<
IMemberEntity
>
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
(
nickname
=>
{
private
allResponses
:
Array
<
IMemberEntity
>
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
(
nickname
=>
{
return
nickname
.
responses
.
filter
(
response
=>
{
return
!!
response
.
value
&&
response
.
value
!==
-
1
?
response
.
value
:
null
;
})[
0
];
...
...
@@ -168,7 +169,7 @@ export class FreeTextExcelWorksheet extends ExcelWorksheet implements IExcelWork
if
(
this
.
responsesWithConfidenceValue
.
length
>
0
)
{
this
.
ws
.
cell
(
8
,
1
).
string
(
this
.
mf
(
'
export.average_confidence
'
)
+
'
:
'
);
let
confidenceSummary
=
0
;
this
.
quiz
.
memberGroups
[
0
].
members
.
forEach
((
nickItem
)
=>
{
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
forEach
((
nickItem
)
=>
{
confidenceSummary
+=
nickItem
.
responses
[
this
.
_questionIndex
].
confidence
;
});
this
.
ws
.
cell
(
8
,
2
).
number
(
Math
.
round
(
confidenceSummary
/
this
.
responsesWithConfidenceValue
.
length
));
...
...
src/export/MultipleChoiceExcelWorksheet.ts
View file @
25a14245
import
MemberDAO
from
'
../db/MemberDAO
'
;
import
{
AbstractAnswerEntity
}
from
'
../entities/answer/AbstractAnswerEntity
'
;
import
{
MultipleChoiceQuestionEntity
}
from
'
../entities/question/MultipleChoiceQuestionEntity
'
;
import
{
IMemberEntity
}
from
'
../interfaces/entities/Member/IMemberEntity
'
;
...
...
@@ -102,7 +103,8 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc
});
this
.
quiz
.
memberGroups
.
forEach
((
memberGroup
)
=>
{
const
responses
=
memberGroup
.
members
.
map
(
nickname
=>
nickname
.
responses
[
this
.
_questionIndex
]);
const
responses
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
).
filter
(
attendee
=>
attendee
.
groupName
===
memberGroup
.
name
)
.
map
(
nickname
=>
nickname
.
responses
[
this
.
_questionIndex
]);
const
hasEntries
:
boolean
=
responses
.
length
>
0
;
const
attendeeEntryRows
:
number
=
hasEntries
?
(
responses
.
length
)
:
1
;
const
attendeeEntryRowStyle
:
Object
=
hasEntries
?
defaultStyles
.
attendeeEntryRowStyle
:
Object
.
assign
({},
defaultStyles
.
attendeeEntryRowStyle
,
...
...
@@ -138,7 +140,7 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc
public
addSheetData
():
void
{
const
answerList
=
this
.
_question
.
answerOptionList
;
const
allResponses
:
Array
<
IMemberEntity
>
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
(
nickname
=>
{
const
allResponses
:
Array
<
IMemberEntity
>
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
(
nickname
=>
{
return
nickname
.
responses
.
map
(
response
=>
{
return
!!
response
.
value
&&
response
.
value
!==
-
1
?
response
.
value
:
null
;
});
...
...
@@ -153,7 +155,7 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc
if
(
this
.
responsesWithConfidenceValue
.
length
>
0
)
{
this
.
ws
.
cell
(
8
,
1
).
string
(
this
.
mf
(
'
export.average_confidence
'
)
+
'
:
'
);
let
confidenceSummary
=
0
;
this
.
quiz
.
memberGroups
[
0
].
members
.
forEach
((
nickItem
)
=>
{
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
forEach
((
nickItem
)
=>
{
confidenceSummary
+=
nickItem
.
responses
[
this
.
_questionIndex
].
confidence
;
});
this
.
ws
.
cell
(
8
,
2
).
number
(
Math
.
round
(
confidenceSummary
/
this
.
responsesWithConfidenceValue
.
length
));
...
...
@@ -182,13 +184,13 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc
nextStartRow
++
;
this
.
ws
.
cell
(
nextStartRow
,
nextColumnIndex
++
).
string
(
responseItem
.
name
);
if
(
this
.
_isCasRequired
)
{
const
profile
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
((
nick
:
IMemberEntity
)
=>
{
const
profile
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
((
nick
:
IMemberEntity
)
=>
{
return
nick
.
name
===
responseItem
.
name
;
})[
0
].
casProfile
;
this
.
ws
.
cell
(
nextStartRow
,
nextColumnIndex
++
).
string
(
profile
.
username
[
0
]);
this
.
ws
.
cell
(
nextStartRow
,
nextColumnIndex
++
).
string
(
profile
.
mail
[
0
]);
}
const
nickItem
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
(
nick
=>
nick
.
name
===
responseItem
.
name
)[
0
];
const
nickItem
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
(
nick
=>
nick
.
name
===
responseItem
.
name
)[
0
];
const
chosenAnswer
=
this
.
_question
.
answerOptionList
.
filter
((
answer
,
index
)
=>
{
const
responseValue
=
nickItem
.
responses
[
this
.
_questionIndex
].
value
;
// noinspection SuspiciousInstanceOfGuard
...
...
src/export/RangedExcelWorksheet.ts
View file @
25a14245
import
MemberDAO
from
'
../db/MemberDAO
'
;
import
{
RangedQuestionEntity
}
from
'
../entities/question/RangedQuestionEntity
'
;
import
{
IMemberEntity
}
from
'
../interfaces/entities/Member/IMemberEntity
'
;
import
{
IExcelWorksheet
}
from
'
../interfaces/iExcel
'
;
...
...
@@ -151,7 +152,7 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh
if
(
this
.
_isCasRequired
)
{
nextColumnIndex
+=
2
;
}
const
responseItem
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
(
nickitem
=>
{
const
responseItem
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
(
nickitem
=>
{
return
nickitem
.
name
===
leaderboardItem
.
name
;
})[
0
].
responses
[
this
.
_questionIndex
];
const
castedQuestion
=
<
RangedQuestionEntity
>
this
.
_question
;
...
...
@@ -216,7 +217,7 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh
if
(
this
.
responsesWithConfidenceValue
.
length
>
0
)
{
this
.
ws
.
cell
(
8
,
1
).
string
(
this
.
mf
(
'
export.average_confidence
'
)
+
'
:
'
);
let
confidenceSummary
=
0
;
this
.
quiz
.
memberGroups
[
0
].
members
.
forEach
((
nickItem
)
=>
{
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
forEach
((
nickItem
)
=>
{
confidenceSummary
+=
nickItem
.
responses
[
this
.
_questionIndex
].
confidence
;
});
this
.
ws
.
cell
(
8
,
2
).
number
(
Math
.
round
(
confidenceSummary
/
this
.
responsesWithConfidenceValue
.
length
));
...
...
@@ -236,7 +237,7 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh
let
nextStartRow
=
10
;
this
.
leaderBoardData
.
forEach
((
leaderboardItem
)
=>
{
const
responseItem
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
(
nickitem
=>
{
const
responseItem
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
(
nickitem
=>
{
return
nickitem
.
name
===
leaderboardItem
.
name
;
})[
0
].
responses
[
this
.
_questionIndex
];
...
...
@@ -244,7 +245,7 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh
nextStartRow
++
;
this
.
ws
.
cell
(
nextStartRow
,
nextColumnIndex
++
).
string
(
leaderboardItem
.
name
);
if
(
this
.
_isCasRequired
)
{
const
profile
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
((
nick
:
IMemberEntity
)
=>
{
const
profile
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
((
nick
:
IMemberEntity
)
=>
{
return
nick
.
name
===
leaderboardItem
.
name
;
})[
0
].
casProfile
;
this
.
ws
.
cell
(
nextStartRow
,
nextColumnIndex
++
).
string
(
profile
.
username
[
0
]);
...
...
src/export/SingleChoiceExcelWorksheet.ts
View file @
25a14245
import
MemberDAO
from
'
../db/MemberDAO
'
;
import
{
AbstractAnswerEntity
}
from
'
../entities/answer/AbstractAnswerEntity
'
;
import
{
SingleChoiceQuestionEntity
}
from
'
../entities/question/SingleChoiceQuestionEntity
'
;
import
{
IMemberEntity
}
from
'
../interfaces/entities/Member/IMemberEntity
'
;
...
...
@@ -100,7 +101,7 @@ export class SingleChoiceExcelWorksheet extends ExcelWorksheet implements IExcel
lastColumn
:
minColums
,
});
const
responses
=
this
.
quiz
.
memberGroups
[
0
].
members
.
map
(
nickname
=>
nickname
.
responses
[
this
.
_questionIndex
]);
const
responses
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
map
(
nickname
=>
nickname
.
responses
[
this
.
_questionIndex
]);
const
hasEntries
:
boolean
=
responses
.
length
>
0
;
const
attendeeEntryRows
:
number
=
hasEntries
?
(
responses
.
length
)
:
1
;
const
attendeeEntryRowStyle
:
any
=
hasEntries
?
defaultStyles
.
attendeeEntryRowStyle
:
Object
.
assign
({},
defaultStyles
.
attendeeEntryRowStyle
,
{
...
...
@@ -148,7 +149,7 @@ export class SingleChoiceExcelWorksheet extends ExcelWorksheet implements IExcel
public
addSheetData
():
void
{
const
answerList
=
this
.
_question
.
answerOptionList
;
const
allResponses
:
Array
<
IMemberEntity
>
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
(
nickname
=>
{
const
allResponses
:
Array
<
IMemberEntity
>
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
(
nickname
=>
{
return
nickname
.
responses
.
map
(
response
=>
{
return
!!
response
.
value
&&
response
.
value
!==
-
1
?
response
.
value
:
null
;
});
...
...
@@ -172,7 +173,7 @@ export class SingleChoiceExcelWorksheet extends ExcelWorksheet implements IExcel
if
(
this
.
responsesWithConfidenceValue
.
length
>
0
)
{
this
.
ws
.
cell
(
8
,
1
).
string
(
this
.
mf
(
'
export.average_confidence
'
)
+
'
:
'
);
let
confidenceSummary
=
0
;
this
.
quiz
.
memberGroups
[
0
].
members
.
forEach
((
nickItem
)
=>
{
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
forEach
((
nickItem
)
=>
{
confidenceSummary
+=
nickItem
.
responses
[
this
.
_questionIndex
].
confidence
;
});
this
.
ws
.
cell
(
8
,
2
).
number
(
Math
.
round
(
confidenceSummary
/
this
.
responsesWithConfidenceValue
.
length
));
...
...
@@ -196,7 +197,7 @@ export class SingleChoiceExcelWorksheet extends ExcelWorksheet implements IExcel
nextStartRow
++
;
this
.
ws
.
cell
(
nextStartRow
,
nextColumnIndex
++
).
string
(
responseItem
.
name
);
if
(
this
.
_isCasRequired
)
{
const
profile
:
any
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
(
nickname
=>
nickname
.
name
===
responseItem
.
name
)[
0
].
casProfile
;
const
profile
:
any
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
(
nickname
=>
nickname
.
name
===
responseItem
.
name
)[
0
].
casProfile
;
this
.
ws
.
cell
(
nextStartRow
,
nextColumnIndex
++
).
string
(
profile
.
username
[
0
]);
// noinspection SuspiciousInstanceOfGuard
this
.
ws
.
cell
(
nextStartRow
,
nextColumnIndex
++
).
string
(
Array
.
isArray
(
profile
.
mail
)
?
profile
.
mail
.
slice
(
-
1
)[
0
]
:
profile
.
mail
);
...
...
src/export/SummaryExcelWorksheet.ts
View file @
25a14245
import
*
as
path
from
'
path
'
;
import
MemberDAO
from
'
../db/MemberDAO
'
;
import
{
AbstractQuestionEntity
}
from
'
../entities/question/AbstractQuestionEntity
'
;
import
{
QuestionType
}
from
'
../enums/QuestionType
'
;
import
{
IMemberEntity
}
from
'
../interfaces/entities/Member/IMemberEntity
'
;
...
...
@@ -169,12 +170,12 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks
public
addSheetData
():
void
{
let
currentRowIndex
=
1
;
const
numberOfResponses
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
(
nickname
=>
{
const
numberOfResponses
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
(
nickname
=>
{
return
nickname
.
responses
.
filter
(
response
=>
{
return
!!
response
.
value
&&
response
.
value
!==
-
1
;
}).
length
;
}).
length
;
const
allResponses
:
Array
<
IMemberEntity
>
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
(
nickname
=>
{
const
allResponses
:
Array
<
IMemberEntity
>
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
(
nickname
=>
{
return
nickname
.
responses
.
map
(
response
=>
{
return
!!
response
.
value
&&
response
.
value
!==
-
1
?
response
.
value
:
null
;
});
...
...
@@ -262,7 +263,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks
const
targetRow
=
indexInList
+
currentRowIndex
;
this
.
ws
.
cell
(
targetRow
,
nextColumnIndex
++
).
string
(
leaderboardItem
.
name
);
if
(
this
.
_isCasRequired
)
{
const
profile
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
((
nick
:
IMemberEntity
)
=>
{
const
profile
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
((
nick
:
IMemberEntity
)
=>
{
return
nick
.
name
===
leaderboardItem
.
name
;
})[
0
].
casProfile
;
this
.
ws
.
cell
(
targetRow
,
nextColumnIndex
++
).
string
(
profile
.
username
[
0
]);
...
...
@@ -303,7 +304,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks
const
targetRow
=
indexInList
+
nextStartRow
;
this
.
ws
.
cell
(
targetRow
,
nextColumnIndex
++
).
string
(
responseItem
.
name
);
if
(
this
.
_isCasRequired
)
{
const
profile
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
((
nick
:
IMemberEntity
)
=>
{
const
profile
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
((
nick
:
IMemberEntity
)
=>
{
return
nick
.
name
===
responseItem
.
name
;
})[
0
].
casProfile
;
this
.
ws
.
cell
(
targetRow
,
nextColumnIndex
++
).
string
(
profile
.
username
[
0
]);
...
...
@@ -331,7 +332,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks
const
leaderBoard
=
new
Leaderboard
();
const
correctResponses
:
any
=
{};
this
.
quiz
.
memberGroups
[
0
].
members
.
forEach
(
attendee
=>
{
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
forEach
(
attendee
=>
{
for
(
let
i
=
0
;
i
<
this
.
quiz
.
questionList
.
length
;
i
++
)
{
const
question
:
AbstractQuestionEntity
=
this
.
quiz
.
questionList
[
i
];
if
(
leaderBoard
.
isCorrectResponse
(
attendee
.
responses
[
i
],
question
)
===
1
)
{
...
...
src/export/SurveyExcelWorksheet.ts
View file @
25a14245
import
MemberDAO
from
'
../db/MemberDAO
'
;
import
{
SurveyQuestionEntity
}
from
'
../entities/question/SurveyQuestionEntity
'
;
import
{
IMemberEntity
}
from
'
../interfaces/entities/Member/IMemberEntity
'
;
import
{
IExcelWorksheet
}
from
'
../interfaces/iExcel
'
;
...
...
@@ -141,7 +142,7 @@ export class SurveyExcelWorksheet extends ExcelWorksheet implements IExcelWorksh
if
(
this
.
responsesWithConfidenceValue
.
length
>
0
)
{
this
.
ws
.
cell
(
7
,
1
).
string
(
this
.
mf
(
'
export.average_confidence
'
)
+
'
:
'
);
let
confidenceSummary
=
0
;
this
.
quiz
.
memberGroups
[
0
].
members
.
forEach
((
nickItem
)
=>
{
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
forEach
((
nickItem
)
=>
{
confidenceSummary
+=
nickItem
.
responses
[
this
.
_questionIndex
].
confidence
;
});
this
.
ws
.
cell
(
7
,
2
).
number
(
Math
.
round
(
confidenceSummary
/
this
.
responsesWithConfidenceValue
.
length
));
...
...
@@ -164,7 +165,7 @@ export class SurveyExcelWorksheet extends ExcelWorksheet implements IExcelWorksh
nextColumnIndex
=
1
;
nextStartRow
++
;
this
.
ws
.
cell
(
nextStartRow
,
nextColumnIndex
++
).
string
(
leaderboardItem
.
name
);
const
nickItem
=
this
.
quiz
.
memberGroups
[
0
].
members
.
filter
((
nick
:
IMemberEntity
)
=>
{
const
nickItem
=
MemberDAO
.
getMembersOfQuiz
(
this
.
quiz
.
name
)
.
filter
((
nick
:
IMemberEntity
)
=>
{
return
nick
.
name
===
leaderboardItem
.
name
;
})[
0
];
if
(
this
.
_isCasRequired
)
{
...
...
src/export/lib/excel_function_library.ts
View file @
25a14245
import
MemberDAO
from
'
../../db/MemberDAO
'
;
import
{
IQuizEntity
}
from
'
../../interfaces/quizzes/IQuizEntity
'
;
export
function
calculateNumberOfAnswers
(
quiz
:
IQuizEntity
,
questionIndex
:
number
,
answerNumber
:
number
):
number
{
let
numberOfAnswers
=
0
;
quiz
.
memberGroups
[
0
].
members
.
forEach
(
nickname
=>
{
MemberDAO
.
getMembersOfQuiz
(
quiz
.
name
)
.
forEach
(
nickname
=>
{
const
response
=
nickname
.
responses
[
questionIndex
].
value
;
// noinspection SuspiciousInstanceOfGuard
if
(
Array
.
isArray
(
response
))
{
...
...
@@ -14,17 +15,18 @@ export function calculateNumberOfAnswers(quiz: IQuizEntity, questionIndex: numbe
return
numberOfAnswers
;
}
export
function
calculateNumberOfRangedAnswers
(
quiz
:
IQuizEntity
,
questionIndex
:
number
,
minRange
,
correctValue
,
maxRange
,
export
function
calculateNumberOfRangedAnswers
(
quiz
:
IQuizEntity
,
questionIndex
:
number
,
minRange
,
correctValue
,
maxRange
,
):
{
minRange
:
number
,
correctValue
:
number
,
maxRange
:
number
}
{
let
numberOfAnswersInMinRange
=
0
;
let
numberOfAnswersInMaxRange
=
0
;
let
numberOfCorrectAnswers
=
0
;
quiz
.
memberGroups
[
0
].
members
.
forEach
((
nickname
)
=>
{
MemberDAO
.
getMembersOfQuiz
(
quiz
.
name
)
.
forEach
((
nickname
)
=>
{
if
(
nickname
.
responses
[
questionIndex
].
value
<=
maxRange
&&
nickname
.
responses
[
questionIndex
].
value
>
correctValue
)
{
numberOfAnswersInMaxRange
++
;
}
else
if
(
nickname
.
responses
[
questionIndex
].
value
===
correctValue
)
{
...
...
src/interfaces/entities/Member/IMemberEntity.ts
View file @
25a14245
import
{
ObjectId
}
from
'
bson
'
;
import
*
as
WebSocket
from
'
ws
'
;
import
{
IQuizResponse
}
from
'
../../quizzes/IQuizResponse
'
;
import
{
ICasData
}
from
'
../../users/ICasData
'
;
import
{
IMemberBase
}
from
'
./IMemberBase
'
;
import
{
IMemberSerialized
}
from
'
./IMemberSerialized
'
;
...
...
@@ -17,4 +18,5 @@ export interface IMemberEntity extends IMemberBase {
setReadingConfirmation
():
void
;
generateResponseForQuiz
(
questionAmount
:
number
):
Array
<
IQuizResponse
>
;
}
src/interfaces/users/IMemberGroupEntity.ts
View file @
25a14245
import
{
ObjectId
}
from
'
bson
'
;
import
{
IMemberEntity
}
from
'
../entities/Member/IMemberEntity
'
;
import
{
IMemberGroupBase
}
from
'
./IMemberGroupBase
'
;
import
{
IMemberGroupSerialized
}
from
'
./IMemberGroupSerialized
'
;
export
interface
IMemberGroupEntity
extends
IMemberGroupBase
{
id
?:
ObjectId
;
members
:
Array
<
IMemberEntity
>
;
members
:
Array
<
string
>
;
serialize
():
IMemberGroupSerialized
;
}
...
...
src/interfaces/users/IMemberGroupSerialized.ts
View file @
25a14245
import
{
IMemberSerialized
}
from
'
../entities/Member/IMemberSerialized
'
;
import
{
IMemberGroupBase
}
from
'
./IMemberGroupBase
'
;
export
interface
IMemberGroupSerialized
extends
IMemberGroupBase
{
members
?:
Array
<
IMemberSerialized
>
;
members
?:
Array
<
string
>
;
}
src/lib/leaderboard/leaderboard.ts
View file @
25a14245
import
MemberDAO
from
'
../../db/MemberDAO
'
;
import
{
FreeTextAnswerEntity
}
from
'
../../entities/answer/FreetextAnwerEntity
'
;
import
{
AbstractChoiceQuestionEntity
}
from
'
../../entities/question/AbstractChoiceQuestionEntity
'
;
import
{
AbstractQuestionEntity
}
from
'
../../entities/question/AbstractQuestionEntity
'
;
...
...
@@ -8,6 +9,7 @@ import { QuestionType } from '../../enums/QuestionType';
import
{
ILeaderBoardItemBase
}
from
'
../../interfaces/leaderboard/ILeaderBoardItemBase
'
;
import
{
IQuizEntity
}
from
'
../../interfaces/quizzes/IQuizEntity
'
;
import
{
IQuizResponse
}
from
'
../../interfaces/quizzes/IQuizResponse
'
;
import
LoggerService
from
'
../../services/LoggerService
'
;
import
{
staticStatistics
}
from
'
../../statistics
'
;
import
{
AbstractLeaderboardScore
}
from
'
./AbstractLeaderboardScore
'
;
import
{
PointBasedLeaderboardScore
}
from
'
./PointBasedLeaderboardScore
'
;
...
...
@@ -93,8 +95,14 @@ export class Leaderboard {
memberAmount
:
memberGroup
.
members
.
length
,
};
memberGroup
.
members
.
forEach
(
attendee
=>
{
for
(
let
i
=
questionIndex
||
0
;
i
<
endIndex
;
i
++
)
{
memberGroup
.
members
.
forEach
(
attendeeName
=>
{
const
attendee
=
MemberDAO
.
getMembersOfQuiz
(
activeQuiz
.
name
).
find
(
quizAttendee
=>
quizAttendee
.
name
===
attendeeName
);
if
(
!
attendee
)
{
LoggerService
.
error
(
`Cannot find member
${
attendeeName
}
in DAO which should be in quiz
${
activeQuiz
.
name
}
`
);
return
;
}
for
(
let
i
=
0
;
i
<
endIndex
;
i
++
)
{
const
question
:
AbstractQuestionEntity
=
activeQuiz
.
questionList
[
i
];
if
([
QuestionType
.
SurveyQuestion
,
QuestionType
.
ABCDSingleChoiceQuestion
].
includes
(
question
.
TYPE
))
{
continue
;
...
...
@@ -124,6 +132,7 @@ export class Leaderboard {
responseTime
:
0
,
correctQuestions
:
[],
confidenceValue
:
0
,
score
:
0
,
};
}
...
...
@@ -135,7 +144,6 @@ export class Leaderboard {
}
else
{
delete
correctResponses
[
attendee
.
name
];
delete
partiallyCorrectResponses
[
attendee
.
name
];
break
;
}
}
...
...
@@ -158,6 +166,20 @@ export class Leaderboard {
};
}
public
sortBy
(
correctResponses
:
Array
<
ILeaderBoardItemBase
>
,
parameter
:
string
):
Array
<
ILeaderBoardItemBase
>
;
public
sortBy
(
correctResponses
:
object
,
parameter
:
string
):
Array
<
ILeaderBoardItemBase
>
;
public
sortBy
(
correctResponses
:
object
|
Array
<
ILeaderBoardItemBase
>
,
parameter
:
string
):
Array
<
ILeaderBoardItemBase
>
{
const
comparator
=
(
a
,
b
)
=>
{
return
a
[
parameter
]
>
b
[
parameter
]
?
1
:
a
[
parameter
]
===
b
[
parameter
]
?
0
:
-
1
;
};
if
(
Array
.
isArray
(
correctResponses
))
{
return
correctResponses
.
sort
(
comparator
);
}
else
{
return
this
.
objectToArray
(
correctResponses
).
sort
(
comparator
);
}
}
private
isCorrectSingleChoiceQuestion
(
response
:
number
,
question
:
AbstractChoiceQuestionEntity
):
boolean
{
return
question
.
answerOptionList
[
response
].
isCorrect
;
}
...
...
src/routers/rest/MemberRouter.ts
View file @
25a14245
...
...
@@ -230,7 +230,7 @@ export class MemberRouter extends AbstractRouter {
}
const
names
:
Array
<
String
>
=
activeQuiz
.
sessionConfig
.
nicks
.
selectedNicks
.
filter
((
nick
)
=>
{
return
!
activeQuiz
.
memberGroups
.
find
(
memberGroup
=>
{
return
!!
memberGroup
.
members
.
find
(
value
=>
value
.
name
===
nick
);
return
!!
memberGroup
.
members
.
find
(
value
=>
value
===
nick
);
});
});
return
{
...
...
src/routers/rest/QuizRouter.ts
View file @
25a14245
...
...
@@ -668,9 +668,9 @@ export class QuizRouter extends AbstractRouter {
status
:
StatusProtocol
.
Success
,
step
:
MessageProtocol
.
GetLeaderboardData
,
payload
:
{
correctResponses
:
this
.
_leaderboard
.
objectToArray
(
correctResponses
),
partiallyCorrectResponses
:
this
.
_leaderboard
.
objectToArray
(
partiallyCorrectResponses
),
memberGroupResults
:
this
.
_leaderboard
.
objectToArray
(
memberGroupResults
),
correctResponses
:
this
.
_leaderboard
.
sortBy
(
correctResponses
,
'
score
'
),
partiallyCorrectResponses
:
this
.
_leaderboard
.
sortBy
(
partiallyCorrectResponses
,
'
score
'
),
memberGroupResults
:
this
.
_leaderboard
.
sortBy
(
memberGroupResults
,
'
score
'
),
},
};
}
...
...
src/routers/websocket/WebSocketRouter.ts
View file @
25a14245
import
*
as
WebSocket
from
'
ws
'
;
import
MemberDAO
from
'
../../db/MemberDAO
'
;
import
QuizDAO
from
'
../../db/quiz/QuizDAO
'
;
import
{
DbEvent
}
from
'
../../enums/DbOperation
'
;
import
{
MessageProtocol
,
StatusProtocol
}
from
'
../../enums/Message
'
;
import
{
WebSocketStatus
}
from
'
../../enums/WebSocketStatus
'
;
import
{
IMessage
}
from
'
../../interfaces/communication/IMessage
'
;
import
{
IMemberEntity
}
from
'
../../interfaces/entities/Member/IMemberEntity
'
;
import
{
IQuizEntity
}
from
'
../../interfaces/quizzes/IQuizEntity
'
;
import
{
IGlobal
}
from
'
../../main
'
;
import
LoggerService
from
'
../../services/LoggerService
'
;
...
...
@@ -33,14 +33,7 @@ export class WebSocketRouter {
}
else
{
res
.
step
=
MessageProtocol
.
AllPlayers
;
res
.
payload
=
{
members
:
activeQuiz
.
memberGroups
.
map
((
memberGroup
)
=>
{
// TODO: this does strange stuff
return
memberGroup
.
members
.
map
((
nickname
:
IMemberEntity
)
=>
{
return
nickname
.
serialize
();
});
}).
reduce
((
previousValue
,
currentValue
)
=>
{
return
previousValue
.
concat
(...
currentValue
);
}),
members
:
MemberDAO
.
getMembersOfQuiz
(
activeQuiz
.
name
).
map
(
nickname
=>
nickname
.
serialize
()),
};
}
ws
.
send
(
JSON
.
stringify
(
res
));
...
...
src/tests/export/export.test.ts
View file @
25a14245
...
...
@@ -8,7 +8,6 @@ import { slow, suite, test } from 'mocha-typescript';
import
*
as
path
from
'
path
'
;
import
QuizDAO
from
'
../../db/quiz/QuizDAO
'
;
import
{
FreeTextAnswerEntity
}
from
'
../../entities/answer/FreetextAnwerEntity
'
;
import
{
MemberEntity
}
from
'
../../entities/member/MemberEntity
'
;
import
{
FreeTextQuestionEntity
}
from
'
../../entities/question/FreeTextQuestionEntity
'
;
import
{
RangedQuestionEntity
}
from
'
../../entities/question/RangedQuestionEntity
'
;
import
{
SurveyQuestionEntity
}
from
'
../../entities/question/SurveyQuestionEntity
'
;
...
...
@@ -114,13 +113,7 @@ class ExcelExportTestSuite {
public
async
addMembers
():
Promise
<
void
>
{
const
quiz
=
QuizDAO
.
getActiveQuizByName
(
this
.
_hashtag
);
for
(
let
memberIndex
=
0
;
memberIndex
<
this
.
_memberCount
;
memberIndex
++
)
{
quiz
.
memberGroups
[
0
].
members
.
push
(
new
MemberEntity
({
id
:
''
,
name
:
`testnick
${
memberIndex
+
1
}
`
,
groupName
:
'
Default
'
,
token
:
'
token
'
,
currentQuizName
:
this
.
_hashtag
,
}));
quiz
.
memberGroups
[
0
].
members
.
push
(
`testnick
${
memberIndex
+
1
}
`
);
}
await
assert
.
equal
(
quiz
.
memberGroups
[
0
].
members
.
length
,
this
.
_memberCount
,
`Expected that the quiz has
${
this
.
_memberCount
}
members`
);
}
...
...
@@ -189,12 +182,14 @@ class ExcelExportTestSuite {
default
:
throw
new
Error
(
`Unsupported question type
${
question
.
TYPE
}
`
);
}
quiz
.
memberGroups
[
0
].
members
[
memberIndex
].
responses
.
push
({
value
:
value
,
responseTime
:
this
.
randomIntFromInterval
(
0
,
quiz
.
questionList
[
questionIndex
].
timer
),
confidence
:
this
.
randomIntFromInterval
(
0
,
100
),
readingConfirmation
:
true
,
});
const
responses
=
[
{
value
:
value
,
responseTime
:
this
.
randomIntFromInterval
(
0
,
quiz
.
questionList
[
questionIndex
].
timer
),
confidence
:
this
.
randomIntFromInterval
(
0
,
100
),
readingConfirmation
:
true
,
},
];
}
}
}
...
...
Write
Preview