Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,9 @@ api.get("/users/status-counts", async (req, res) => {
);
const userActivities = dbFetchedActivity.rows;

const rawUsers = await db.query("SELECT user_id FROM all_users");
const rawUsers = await db.query(
"SELECT user_id,display_name_normalised,email FROM all_users",
);
const allusers = rawUsers.rows;

const rawConfigTable = await db.query("SELECT * FROM config_table");
Expand Down
43 changes: 24 additions & 19 deletions api/functions/decideStatus.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,46 @@
import logger from "../utils/logger.js";

/**
* Determines the status of users based on their normalised scores.
* Determines the activity status of users based on their normalised scores.
*
* This function takes an array of normalised scores and compares each score with
* the provided thresholds in the `configTable`. It categorises users into
* different status groups: inactive, low, medium, and high activity based on
* the score ranges defined in the `configTable`.
* This function takes an array of user objects (each containing `userId` and normalised `score`)
* and categorises them into activity levels based on threshold values provided in the `configTable`.
* The categories are: `inactive`, `low`, `medium`, and `high`.
*
* @param {number[]} normalisedScores - An array of normalised scores representing user activity levels.
* @param {Object} configTable - An object containing threshold values for determining user status.
* @param {number} configTable.low_threshold - The threshold below which users are considered inactive.
* @param {number} configTable.medium_threshold - The threshold for users to be categorised as low activity.
* @param {number} configTable.high_threshold - The threshold above which users are considered to have high activity.
* @param {Array<{userId: string|number, score: number}>} normalisedUser -
* An array of user objects with userId and their normalised score (0–100).
* @param {Object} configTable - Configuration object containing threshold values for activity levels.
* @param {number} configTable.low_threshold - Scores below this are considered 'inactive'.
* @param {number} configTable.medium_threshold - Scores between `low` and this are 'low' activity.
* @param {number} configTable.high_threshold - Scores between `medium` and this are 'medium' activity;
* scores above this are 'high' activity.
*
* @returns {Object} An object representing the count of users in each activity status category:
* `inactive`, `low`, `medium`, and `high`.
* Example: `{ inactive: 5, low: 10, medium: 15, high: 20 }`.
* `{ inactive: number, low: number, medium: number, high: number }`
*
* @throws {Error} Throws an error if there is an issue during the status determination process.
* @throws {Error} Throws an error if status determination fails.
*/
export const decideStatus = async (normalisedScores, configTable) => {
export const decideStatus = async (normalisedUser, configTable) => {
try {
const usersByStatus = { low: [], medium: [], high: [], inactive: [] };
const finalStatus = { low: 0, medium: 0, high: 0, inactive: 0 };
for (const score of normalisedScores) {
if (score < configTable.low_threshold) {
for (const user of normalisedUser) {
if (user.score < configTable.low_threshold) {
finalStatus.inactive += 1;
} else if (score < configTable.medium_threshold) {
usersByStatus.inactive.push(user);
} else if (user.score < configTable.medium_threshold) {
finalStatus.low += 1;
} else if (score < configTable.high_threshold) {
usersByStatus.low.push(user);
} else if (user.score < configTable.high_threshold) {
finalStatus.medium += 1;
usersByStatus.medium.push(user);
} else {
finalStatus.high += 1;
usersByStatus.high.push(user);
}
}

return finalStatus;
return { finalStatus, usersByStatus };
} catch (error) {
logger.error(error);
throw error;
Expand Down
5 changes: 3 additions & 2 deletions api/functions/getBoxPlotData.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
* @param {number[]} scores - An array of numerical scores to analyse.
* @returns {Object|null} An object containing min, Q1, median, Q3, max, and outliers, or null if the array is empty.
*/
export const getBoxPlotData = (scores) => {
if (!scores.length) return null;
export const getBoxPlotData = (userScores) => {
if (!userScores.length) return null;

const scores = userScores.map((user) => user.score);
const sorted = [...scores].sort((a, b) => a - b);
const n = sorted.length;

Expand Down
28 changes: 19 additions & 9 deletions api/functions/scoreNormaliser.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,31 @@ const decideTotalScore = (userId, userActivity, configTable) => {
* Normalises the activity scores of a list of users to a 0–100 scale,
* where the highest score becomes 100 and others are scaled relative to it.
*
* Each returned object links the normalised score to its corresponding user ID.
*
* @param {Array<Object>} usersArray - Array of user objects, each containing at least a `user_id` property.
* @param {Object} userActivity - An object containing raw activity data for users.
* @param {Object} configTable - Configuration object that includes the weighting of each activity type.
* @returns {Array<number>} - An array of normalised scores between 0 and 100.
* @returns {Array<{userId: string|number, score: number}>} - An array of objects with userId and their normalised score (0–100).
*/
export const scoreNormaliser = (usersArray, userActivity, configTable) => {
const scoreArray = [];
for (const user of usersArray) {
scoreArray.push(decideTotalScore(user.user_id, userActivity, configTable));
}
const scoredUsers = usersArray.map((user) => {
const score = decideTotalScore(user.user_id, userActivity, configTable);
return {
userId: user.user_id,
name: user.name,
email: user.email,
score,
};
});

const maxScore = Math.max(...scoreArray);
const normalisedScores = scoreArray.map((score) =>
Math.round((score / maxScore) * 100),
);
const maxScore = Math.max(...scoredUsers.map((u) => u.score));
const normalisedScores = scoredUsers.map((user) => {
return {
...user,
score: Math.round((user.score / maxScore) * 100),
};
});

return normalisedScores;
};