refactor learning dashboard to use Reactions instead of Emojis

This commit is contained in:
Gustavo Trott 2024-08-26 13:40:17 -03:00
parent 09f565d0ef
commit 736c9a130f
10 changed files with 123 additions and 228 deletions

View File

@ -41,8 +41,9 @@ case class User(
answers: Map[String,Vector[String]] = Map(),
genericData: Map[String, Vector[GenericData]] = Map(),
talk: Talk = Talk(),
emojis: Vector[Emoji] = Vector(),
reactions: Vector[Emoji] = Vector(),
reactions: Vector[Reaction] = Vector(),
raiseHand: Vector[Long] = Vector(),
away: Vector[Away] = Vector(),
webcams: Vector[Webcam] = Vector(),
totalOfMessages: Long = 0,
)
@ -75,11 +76,16 @@ case class Talk(
lastTalkStartedOn: Long = 0,
)
case class Emoji(
case class Reaction(
name: String,
sentOn: Long = System.currentTimeMillis(),
)
case class Away(
startedOn: Long = System.currentTimeMillis(),
stoppedOn: Long = 0,
)
case class Webcam(
startedOn: Long = System.currentTimeMillis(),
stoppedOn: Long = 0,
@ -366,7 +372,7 @@ class LearningDashboardActor(
user <- findUserByIntId(meeting, msg.body.userId)
} yield {
if (msg.body.raiseHand) {
val updatedUser = user.copy(emojis = user.emojis :+ Emoji("raiseHand"))
val updatedUser = user.copy(raiseHand = user.raiseHand :+ System.currentTimeMillis())
val updatedMeeting = meeting.copy(users = meeting.users + (updatedUser.userKey -> updatedUser))
meetings += (updatedMeeting.intId -> updatedMeeting)
@ -379,12 +385,15 @@ class LearningDashboardActor(
meeting <- meetings.values.find(m => m.intId == msg.header.meetingId)
user <- findUserByIntId(meeting, msg.body.userId)
} yield {
if (msg.body.away) {
val updatedUser = user.copy(emojis = user.emojis :+ Emoji("away"))
val updatedMeeting = meeting.copy(users = meeting.users + (updatedUser.userKey -> updatedUser))
meetings += (updatedMeeting.intId -> updatedMeeting)
val updatedUser = if (msg.body.away) {
user.copy(away = user.away :+ Away())
} else {
val lastAway: Away = user.away.last.copy(stoppedOn = System.currentTimeMillis())
user.copy(away = user.away.dropRight(1) :+ lastAway)
}
val updatedMeeting = meeting.copy(users = meeting.users + (updatedUser.userKey -> updatedUser))
meetings += (updatedMeeting.intId -> updatedMeeting)
}
}
@ -400,26 +409,9 @@ class LearningDashboardActor(
}).length > 0
if(!hasSameReactionInLast30Seconds) {
val updatedUser = user.copy(reactions = user.reactions :+ Emoji(msg.body.reactionEmoji))
val updatedUser = user.copy(reactions = user.reactions :+ Reaction(msg.body.reactionEmoji))
val updatedMeeting = meeting.copy(users = meeting.users + (updatedUser.userKey -> updatedUser))
meetings += (updatedMeeting.intId -> updatedMeeting)
//Convert Reactions to legacy Emoji (while LearningDashboard doesn't support Reactions)
val emoji = msg.body.reactionEmoji.codePointAt(0) match {
case 128515 => "happy"
case 128528 => "neutral"
case 128577 => "sad"
case 128077 => "thumbsUp"
case 128078 => "thumbsDown"
case 128079 => "applause"
case _ => "none"
}
if (emoji != "none") {
val updatedUserWithEmoji = updatedUser.copy(emojis = user.emojis :+ Emoji(emoji))
val updatedMeetingWithEmoji = meeting.copy(users = meeting.users + (updatedUserWithEmoji.userKey -> updatedUserWithEmoji))
meetings += (updatedMeeting.intId -> updatedMeetingWithEmoji)
}
}
}
}

View File

@ -21,6 +21,8 @@ module.exports = {
'import/no-absolute-path': 0,
'import/no-unresolved': 0,
'import/no-extraneous-dependencies': 1,
'import/no-named-as-default': 0,
'import/no-named-as-default-member': 0,
'react/prop-types': 'off',
'jsx-a11y/no-access-key': 0,
'react/jsx-props-no-spreading': 'off',

View File

@ -10,7 +10,6 @@ import './bbb-icons.css';
import {
FormattedMessage, FormattedDate, injectIntl, FormattedTime,
} from 'react-intl';
import { emojiConfigs } from './services/EmojiService';
import CardBody from './components/Card';
import UsersTable from './components/UsersTable';
import UserDetails from './components/UserDetails/component';
@ -125,38 +124,31 @@ class App extends React.Component {
});
}
fetchMostUsedEmojis() {
fetchMostUsedReactions() {
const { activitiesJson } = this.state;
if (!activitiesJson) { return []; }
// Icon elements
const emojis = [...Object.keys(emojiConfigs)];
const icons = {};
emojis.forEach((emoji) => {
icons[emoji] = (<i className={`${emojiConfigs[emoji].icon} bbb-icon-card`} />);
});
// Count each emoji
const emojiCount = {};
emojis.forEach((emoji) => {
emojiCount[emoji] = 0;
});
const allEmojisUsed = Object
// Count each reaction
const reactionCount = {};
const allReactionsUsed = Object
.values(activitiesJson.users || {})
.map((user) => user.emojis || [])
.map((user) => user.reactions || [])
.flat(1);
allEmojisUsed.forEach((emoji) => {
emojiCount[emoji.name] += 1;
allReactionsUsed.forEach((reaction) => {
if (typeof reactionCount[reaction.name] === 'undefined') {
reactionCount[reaction.name] = 0;
}
reactionCount[reaction.name] += 1;
});
// Get the three most used
const mostUsedEmojis = Object
.entries(emojiCount)
const mostUsedReactions = Object
.entries(reactionCount)
.filter(([, count]) => count)
.sort(([, countA], [, countB]) => countA - countB)
.reverse()
.slice(0, 3);
return mostUsedEmojis.map(([emoji]) => icons[emoji]);
return mostUsedReactions.map(([reaction]) => reaction);
}
updateModalUser() {
@ -245,14 +237,13 @@ class App extends React.Component {
) => genericDataListForSpecificUser?.columnTitle);
// This line will eliminate duplicates.
const genericDataColumnTitleList = [...new Set(genericDataColumnTitleWithDuplicates)];
console.log('teste aqui ----> ', activitiesJson, genericDataColumnTitleList);
document.title = `${intl.formatMessage({ id: 'app.learningDashboard.bigbluebuttonTitle', defaultMessage: 'BigBlueButton' })} - ${intl.formatMessage({ id: 'app.learningDashboard.dashboardTitle', defaultMessage: 'Learning Analytics Dashboard' })} - ${activitiesJson.name}`;
function totalOfEmojis() {
function totalOfReactions() {
if (activitiesJson && activitiesJson.users) {
return Object.values(activitiesJson.users)
.reduce((prevVal, elem) => prevVal + elem.emojis.length, 0);
.reduce((prevVal, elem) => prevVal + elem.reactions.length, 0);
}
return 0;
}
@ -303,19 +294,19 @@ class App extends React.Component {
}
// Calculate points of Raise hand
const usersRaiseHand = allUsers.map((currUser) => currUser.emojis.filter((emoji) => emoji.name === 'raiseHand').length);
const usersRaiseHand = allUsers.map((currUser) => currUser.raiseHand.length);
const maxRaiseHand = Math.max(...usersRaiseHand);
const totalRaiseHand = usersRaiseHand.reduce((prev, val) => prev + val, 0);
if (maxRaiseHand > 0) {
meetingAveragePoints += ((totalRaiseHand / nrOfUsers) / maxRaiseHand) * 2;
}
// Calculate points of Emojis
const usersEmojis = allUsers.map((currUser) => currUser.emojis.filter((emoji) => emoji.name !== 'raiseHand').length);
const maxEmojis = Math.max(...usersEmojis);
const totalEmojis = usersEmojis.reduce((prev, val) => prev + val, 0);
if (maxEmojis > 0) {
meetingAveragePoints += ((totalEmojis / nrOfUsers) / maxEmojis) * 2;
// Calculate points of Reactions
const usersReactions = allUsers.map((currUser) => currUser.reactions.length);
const maxReactions = Math.max(...usersReactions);
const totalReactions = usersReactions.reduce((prev, val) => prev + val, 0);
if (maxReactions > 0) {
meetingAveragePoints += ((totalReactions / nrOfUsers) / maxReactions) * 2;
}
// Calculate points of Polls
@ -482,11 +473,11 @@ class App extends React.Component {
<CardContent classes={{ root: '!p-0' }}>
<CardBody
name={intl.formatMessage({ id: 'app.learningDashboard.indicators.timeline', defaultMessage: 'Timeline' })}
number={totalOfEmojis()}
number={totalOfReactions()}
cardClass={tab === TABS.TIMELINE ? 'border-purple-500' : 'hover:border-purple-500 border-white'}
iconClass="bg-purple-200 text-purple-500"
>
{this.fetchMostUsedEmojis()}
{this.fetchMostUsedReactions()}
</CardBody>
</CardContent>
</Card>

View File

@ -1,6 +1,6 @@
import React from 'react';
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
import { emojiConfigs, filterUserEmojis } from '../services/EmojiService';
import { filterUserReactions } from '../services/ReactionService';
import UserAvatar from './UserAvatar';
const intlMessages = defineMessages({
@ -24,31 +24,31 @@ const intlMessages = defineMessages({
class StatusTable extends React.Component {
componentDidMount() {
// This code is needed to prevent emojis from overflowing.
const emojis = document.getElementsByClassName('timeline-emoji');
for (let i = 0; i < emojis.length; i += 1) {
const emojiStyle = window.getComputedStyle(emojis[i]);
const offsetLeft = Number(emojiStyle
// This code is needed to prevent reactions from overflowing.
const reactions = document.getElementsByClassName('timeline-reaction');
for (let i = 0; i < reactions.length; i += 1) {
const reactionStyle = window.getComputedStyle(reactions[i]);
const offsetLeft = Number(reactionStyle
.left
.replace(/px/g, '')
.trim());
if (offsetLeft < 0) {
emojis[i].style.left = '0';
reactions[i].style.left = '0';
}
}
}
componentDidUpdate() {
// This code is needed to prevent emojis from overflowing.
const emojis = document.getElementsByClassName('timeline-emoji');
for (let i = 0; i < emojis.length; i += 1) {
const emojiStyle = window.getComputedStyle(emojis[i]);
const offsetLeft = Number(emojiStyle
// This code is needed to prevent reactions from overflowing.
const reactions = document.getElementsByClassName('timeline-reaction');
for (let i = 0; i < reactions.length; i += 1) {
const reactionStyle = window.getComputedStyle(reactions[i]);
const offsetLeft = Number(reactionStyle
.left
.replace(/px/g, '')
.trim());
if (offsetLeft < 0) {
emojis[i].style.left = '0';
reactions[i].style.left = '0';
}
}
}
@ -287,8 +287,7 @@ class StatusTable extends React.Component {
{ usersPeriods[user.userKey].length > 0 ? (
usersPeriods[user.userKey].map((userPeriod) => {
const { registeredOn, leftOn } = userPeriod;
const userEmojisInPeriod = filterUserEmojis(user,
null,
const userReactionsInPeriod = filterUserReactions(user,
registeredOn >= boundaryLeft && registeredOn <= boundaryRight
? registeredOn : boundaryLeft,
leftOn >= boundaryLeft && leftOn <= boundaryRight
@ -301,34 +300,21 @@ class StatusTable extends React.Component {
|| (boundaryLeft >= registeredOn && leftOn === 0)
? makeLineThrough(userPeriod, period)
: null }
{ userEmojisInPeriod.map((emoji) => {
const offset = ((emoji.sentOn - period.start) * 100)
{ userReactionsInPeriod.map((reaction) => {
const offset = ((reaction.sentOn - period.start) * 100)
/ (interval);
const origin = isRTL ? 'right' : 'left';
const redress = '(0.875rem / 2 + 0.25rem + 2px)';
return (
<div
className="flex absolute p-1 border-white border-2 rounded-full text-sm z-20 bg-purple-500 text-purple-200 timeline-emoji"
className="flex absolute p-1 border-white border-2 rounded-full text-sm z-20 bg-purple-500 text-purple-200 timeline-reaction select-none"
role="generic"
aria-label={intl.formatMessage({
id: emojiConfigs[emoji.name].intlId,
defaultMessage: emojiConfigs[emoji.name].defaultMessage,
})}
style={{
top: `calc(50% - ${redress})`,
[origin]: `calc(${offset}% - ${redress})`,
}}
title={intl.formatMessage({
id: emojiConfigs[emoji.name].intlId,
defaultMessage: emojiConfigs[emoji.name].defaultMessage,
})}
>
<i
className={
'text-sm bbb-icon-timeline'
+ ` ${emojiConfigs[emoji.name].icon}`
}
/>
{reaction.name}
</div>
);
}) }

View File

@ -109,8 +109,8 @@ const UserDatailsComponent = (props) => {
const usersTalkTime = allUsersArr.map((currUser) => currUser.talk.totalTime);
const usersMessages = allUsersArr.map((currUser) => currUser.totalOfMessages);
const usersEmojis = allUsersArr.map((currUser) => currUser.emojis.filter((emoji) => emoji.name !== 'raiseHand').length);
const usersRaiseHands = allUsersArr.map((currUser) => currUser.emojis.filter((emoji) => emoji.name === 'raiseHand').length);
const usersReactions = allUsersArr.map((currUser) => currUser.reactions.length);
const usersRaiseHands = allUsersArr.map((currUser) => currUser.raiseHand.length);
const usersAnswers = allUsersArr.map((currUser) => Object.values(currUser.answers || {}).length);
const totalPolls = Object.values(polls || {}).length;
@ -132,18 +132,18 @@ const UserDatailsComponent = (props) => {
function getPointsOfRaiseHand(u) {
const maxRaiseHand = Math.max(...usersRaiseHands);
const userRaiseHand = u.emojis.filter((emoji) => emoji.name === 'raiseHand').length;
const userRaiseHand = u.reactions.length;
if (maxRaiseHand > 0) {
return (userRaiseHand / maxRaiseHand) * 2;
}
return 0;
}
function getPointsofEmoji(u) {
const maxEmojis = Math.max(...usersEmojis);
const userEmojis = u.emojis.filter((emoji) => emoji.name !== 'raiseHand').length;
if (maxEmojis > 0) {
return (userEmojis / maxEmojis) * 2;
function getPointsofReaction(u) {
const maxReactions = Math.max(...usersReactions);
const userReactions = u.reactions.length;
if (maxReactions > 0) {
return (userReactions / maxReactions) * 2;
}
return 0;
}
@ -161,7 +161,7 @@ const UserDatailsComponent = (props) => {
const messagesAverage = usersMessages
.reduce((prev, curr) => prev + curr, 0) / (allUsersArr.length || 1);
const emojisAverage = usersEmojis
const reactionsAverage = usersReactions
.reduce((prev, curr) => prev + curr, 0) / (allUsersArr.length || 1);
const raiseHandsAverage = usersRaiseHands
@ -173,7 +173,7 @@ const UserDatailsComponent = (props) => {
const activityPointsFunctions = {
'Talk Time': getPointsOfTalk,
Messages: getPointsOfChatting,
Emojis: getPointsofEmoji,
Reactions: getPointsofReaction,
'Raise Hands': getPointsOfRaiseHand,
'Poll Votes': getPointsOfPolls,
};
@ -181,7 +181,7 @@ const UserDatailsComponent = (props) => {
const averages = {
'Talk Time': talkTimeAverage,
Messages: messagesAverage,
Emojis: emojisAverage,
Reactions: reactionsAverage,
'Raise Hands': raiseHandsAverage,
'Poll Votes': pollsAverage,
};
@ -436,7 +436,7 @@ const UserDatailsComponent = (props) => {
<th aria-label="Average" className="grow text-center font-normal"><FormattedMessage id="app.learningDashboard.userDetails.average" defaultMessage="Average" /></th>
<th aria-label="Activity Points" className="min-w-[20%] text-ellipsis text-right rtl:text-left font-normal"><FormattedMessage id="app.learningDashboard.userDetails.activityPoints" defaultMessage="Activity Points" /></th>
</tr>
{ ['Talk Time', 'Messages', 'Emojis', 'Raise Hands', 'Poll Votes'].map((category) => {
{ ['Talk Time', 'Messages', 'Reactions', 'Raise Hands', 'Poll Votes'].map((category) => {
let totalOfActivity = 0;
switch (category) {
@ -446,11 +446,11 @@ const UserDatailsComponent = (props) => {
case 'Messages':
totalOfActivity = user.totalOfMessages;
break;
case 'Emojis':
totalOfActivity = user.emojis.filter((emoji) => emoji.name !== 'raiseHand').length;
case 'Reactions':
totalOfActivity = user.reactions.length;
break;
case 'Raise Hands':
totalOfActivity = user.emojis.filter((emoji) => emoji.name === 'raiseHand').length;
totalOfActivity = user.raiseHand.length;
break;
case 'Poll Votes':
totalOfActivity = Object.values(user.answers).length;

View File

@ -2,7 +2,7 @@ import React from 'react';
import {
FormattedMessage, FormattedDate, FormattedNumber, injectIntl,
} from 'react-intl';
import { getUserEmojisSummary, emojiConfigs } from '../services/EmojiService';
import { getUserReactionsSummary } from '../services/ReactionService';
import { getActivityScore, getSumOfTime, tsToHHmmss } from '../services/UserService';
import UserAvatar from './UserAvatar';
import { UserDetailsContext } from './UserDetails/context';
@ -73,9 +73,9 @@ class UsersTable extends React.Component {
talkTimeOrder, webcamTimeOrder, lastFieldClicked,
} = this.state;
const usersEmojisSummary = {};
const usersReactionsSummary = {};
Object.values(allUsers || {}).forEach((user) => {
usersEmojisSummary[user.userKey] = getUserEmojisSummary(user, 'raiseHand');
usersReactionsSummary[user.userKey] = getUserReactionsSummary(user);
});
function getOnlinePercentage(registeredOn, leftOn) {
@ -206,7 +206,7 @@ class UsersTable extends React.Component {
<FormattedMessage id="app.learningDashboard.usersTable.colMessages" defaultMessage="Messages" />
</th>
<th className="px-3.5 2xl:px-4 py-3 col-text-left">
<FormattedMessage id="app.learningDashboard.usersTable.colEmojis" defaultMessage="Emojis" />
<FormattedMessage id="app.learningDashboard.usersTable.colReactions" defaultMessage="Reactions" />
</th>
<th className="px-3.5 2xl:px-4 py-3 text-center">
<FormattedMessage id="app.learningDashboard.usersTable.colRaiseHands" defaultMessage="Raise Hand" />
@ -429,42 +429,25 @@ class UsersTable extends React.Component {
</span>
) : null }
</td>
<td className={`px-4 py-3 text-sm col-text-left ${opacity}`} data-test="userTotalEmojisDashboard">
<td className={`px-4 py-3 text-sm col-text-left ${opacity}`} data-test="userTotalReactionsDashboard">
{
Object.keys(usersEmojisSummary[user.userKey] || {}).map((emoji) => (
Object.keys(usersReactionsSummary[user.userKey] || {}).map((reaction) => (
<div className="text-xs whitespace-nowrap">
<i className={`${emojiConfigs[emoji].icon} text-sm`} />
{reaction}
&nbsp;
{ usersEmojisSummary[user.userKey][emoji] }
{ usersReactionsSummary[user.userKey][reaction] }
&nbsp;
<FormattedMessage
id={emojiConfigs[emoji].intlId}
defaultMessage={emojiConfigs[emoji].defaultMessage}
/>
</div>
))
}
</td>
<td className={`px-4 py-3 text-sm text-center ${opacity}`} data-test="userRaiseHandDashboard">
{ user.emojis.filter((emoji) => emoji.name === 'raiseHand').length > 0
{ user.raiseHand.length > 0
? (
<span>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4 inline"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M7 11.5V14m0-2.5v-6a1.5 1.5 0 113 0m-3 6a1.5 1.5 0 00-3 0v2a7.5 7.5 0 0015 0v-5a1.5 1.5 0 00-3 0m-6-3V11m0-5.5v-1a1.5 1.5 0 013 0v1m0 0V11m0-5.5a1.5 1.5 0 013 0v3m0 0V11"
/>
</svg>
&nbsp;
{user.emojis.filter((emoji) => emoji.name === 'raiseHand').length}
{user.raiseHand.length}
</span>
) : null }
</td>

View File

@ -1,74 +0,0 @@
export const emojiConfigs = {
away: {
icon: 'icon-bbb-time',
intlId: 'app.actionsBar.emojiMenu.awayLabel',
defaultMessage: 'Away',
},
neutral: {
icon: 'icon-bbb-undecided',
intlId: 'app.actionsBar.emojiMenu.neutralLabel',
defaultMessage: 'Undecided',
},
confused: {
icon: 'icon-bbb-confused',
intlId: 'app.actionsBar.emojiMenu.confusedLabel',
defaultMessage: 'Confused',
},
sad: {
icon: 'icon-bbb-sad',
intlId: 'app.actionsBar.emojiMenu.sadLabel',
defaultMessage: 'Sad',
},
happy: {
icon: 'icon-bbb-happy',
intlId: 'app.actionsBar.emojiMenu.happyLabel',
defaultMessage: 'Happy',
},
applause: {
icon: 'icon-bbb-applause',
intlId: 'app.actionsBar.emojiMenu.applauseLabel',
defaultMessage: 'Applaud',
},
thumbsUp: {
icon: 'icon-bbb-thumbs_up',
intlId: 'app.actionsBar.emojiMenu.thumbsUpLabel',
defaultMessage: 'Thumbs up',
},
thumbsDown: {
icon: 'icon-bbb-thumbs_down',
intlId: 'app.actionsBar.emojiMenu.thumbsDownLabel',
defaultMessage: 'Thumbs down',
},
raiseHand: {
icon: 'icon-bbb-hand',
intlId: 'app.actionsBar.emojiMenu.raiseHandLabel',
defaultMessage: 'Raise hand',
},
};
export function getUserEmojisSummary(user, skipNames = null, start = null, end = null) {
const userEmojis = {};
user.emojis.forEach((emoji) => {
if (typeof emojiConfigs[emoji.name] === 'undefined') return;
if (skipNames != null && skipNames.split(',').indexOf(emoji.name) > -1) return;
if (start != null && emoji.sentOn < start) return;
if (end != null && emoji.sentOn > end) return;
if (typeof userEmojis[emoji.name] === 'undefined') {
userEmojis[emoji.name] = 0;
}
userEmojis[emoji.name] += 1;
});
return userEmojis;
}
export function filterUserEmojis(user, skipNames = null, start = null, end = null) {
const userEmojis = [];
user.emojis.forEach((emoji) => {
if (typeof emojiConfigs[emoji.name] === 'undefined') return;
if (skipNames != null && skipNames.split(',').indexOf(emoji.name) > -1) return;
if (start != null && emoji.sentOn < start) return;
if (end != null && emoji.sentOn > end) return;
userEmojis.push(emoji);
});
return userEmojis;
}

View File

@ -0,0 +1,20 @@
export function getUserReactionsSummary(user) {
const userReactions = {};
user.reactions.forEach((reaction) => {
if (typeof userReactions[reaction.name] === 'undefined') {
userReactions[reaction.name] = 0;
}
userReactions[reaction.name] += 1;
});
return userReactions;
}
export function filterUserReactions(user, start = null, end = null) {
const userReactions = [];
user.reactions.forEach((reaction) => {
if (start != null && reaction.sentOn < start) return;
if (end != null && reaction.sentOn > end) return;
userReactions.push(reaction);
});
return userReactions;
}

View File

@ -1,4 +1,4 @@
import { emojiConfigs, filterUserEmojis } from './EmojiService';
import { filterUserReactions } from './ReactionService';
export function getActivityScore(user, allUsers, totalOfPolls) {
if (user.isModerator) return 0;
@ -21,19 +21,19 @@ export function getActivityScore(user, allUsers, totalOfPolls) {
}
// Calculate points of Raise hand
const usersRaiseHand = allUsersArr.map((currUser) => currUser.emojis.filter((emoji) => emoji.name === 'raiseHand').length);
const usersRaiseHand = allUsersArr.map((currUser) => currUser.raiseHand.length);
const maxRaiseHand = Math.max(...usersRaiseHand);
const userRaiseHand = user.emojis.filter((emoji) => emoji.name === 'raiseHand').length;
const userRaiseHand = user.raiseHand.length;
if (maxRaiseHand > 0) {
userPoints += (userRaiseHand / maxRaiseHand) * 2;
}
// Calculate points of Emojis
const usersEmojis = allUsersArr.map((currUser) => currUser.emojis.filter((emoji) => emoji.name !== 'raiseHand').length);
const maxEmojis = Math.max(...usersEmojis);
const userEmojis = user.emojis.filter((emoji) => emoji.name !== 'raiseHand').length;
if (maxEmojis > 0) {
userPoints += (userEmojis / maxEmojis) * 2;
// Calculate points of Reactions
const usersReactions = allUsersArr.map((currUser) => currUser.reactions.length);
const maxReactions = Math.max(...usersReactions);
const userReactions = user.reactions.length;
if (maxReactions > 0) {
userPoints += (userReactions / maxReactions) * 2;
}
// Calculate points of Polls
@ -101,8 +101,8 @@ const tableHeaderFields = [
defaultMessage: 'Messages',
},
{
id: 'colEmojis',
defaultMessage: 'Emojis',
id: 'colReactions',
defaultMessage: 'Reactions',
},
{
id: 'pollVotes',
@ -130,10 +130,6 @@ export function makeUserCSVData(users, polls, intl) {
const userRecords = {};
const userValues = Object.values(users || {});
const pollValues = Object.values(polls || {});
const skipEmojis = Object
.keys(emojiConfigs)
.filter((emoji) => emoji !== 'raiseHand')
.join(',');
for (let i = 0; i < userValues.length; i += 1) {
const user = userValues[i];
@ -155,9 +151,9 @@ export function makeUserCSVData(users, polls, intl) {
talk: user.talk.totalTime > 0 ? tsToHHmmss(user.talk.totalTime) : '-',
webcam: webcam > 0 ? tsToHHmmss(webcam) : '-',
messages: user.totalOfMessages,
raiseHand: filterUserEmojis(user, 'raiseHand').length,
raiseHand: user.raiseHand.length,
answers: Object.keys(user.answers).length,
emojis: filterUserEmojis(user, skipEmojis).length,
reactions: filterUserReactions(user).length,
registeredOn: intl.formatDate(joinTime, {
year: 'numeric',
month: 'numeric',

View File

@ -1372,7 +1372,6 @@
"app.learningDashboard.userDetails.anonymousAnswer": "Anonymous Poll",
"app.learningDashboard.userDetails.talkTime": "Talk Time",
"app.learningDashboard.userDetails.messages": "Messages",
"app.learningDashboard.userDetails.emojis": "Emojis",
"app.learningDashboard.userDetails.raiseHands": "Raise Hands",
"app.learningDashboard.userDetails.pollVotes": "Poll Votes",
"app.learningDashboard.userDetails.onlineIndicator": "{0} online time",
@ -1381,7 +1380,7 @@
"app.learningDashboard.usersTable.colTalk": "Talk time",
"app.learningDashboard.usersTable.colWebcam": "Webcam Time",
"app.learningDashboard.usersTable.colMessages": "Messages",
"app.learningDashboard.usersTable.colEmojis": "Emojis",
"app.learningDashboard.usersTable.colReactions": "Reactions",
"app.learningDashboard.usersTable.colRaiseHands": "Raise Hands",
"app.learningDashboard.usersTable.colActivityScore": "Activity Score",
"app.learningDashboard.usersTable.colStatus": "Status",