Skip to content

Commit

Permalink
Merge pull request #4078 from airqo-platform/hf-preferences-updates-1
Browse files Browse the repository at this point in the history
fix: update preference job to handle compound unique key with group_id
  • Loading branch information
Baalmart authored Dec 14, 2024
2 parents 9a63d39 + 8d97388 commit 331742e
Showing 1 changed file with 87 additions and 19 deletions.
106 changes: 87 additions & 19 deletions src/auth-service/bin/jobs/preferences-update-job.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const cron = require("node-cron");
const UserModel = require("@models/User");
const mongoose = require("mongoose");
const PreferenceModel = require("@models/Preference");
const SelectedSiteModel = require("@models/SelectedSite");
const constants = require("@config/constants");
Expand All @@ -12,6 +13,30 @@ const stringify = require("@utils/stringify");
const isEmpty = require("is-empty");
const BATCH_SIZE = 100;

// Function to validate critical default values
const validateDefaultValues = () => {
const criticalDefaults = [
{ key: "DEFAULT_GROUP", value: constants.DEFAULT_GROUP },
{ key: "DEFAULT_AIRQLOUD", value: constants.DEFAULT_AIRQLOUD },
{ key: "DEFAULT_GRID", value: constants.DEFAULT_GRID },
{ key: "DEFAULT_NETWORK", value: constants.DEFAULT_NETWORK },
];

const missingDefaults = criticalDefaults.filter(
(item) => isEmpty(item.value) || item.value === undefined
);

if (missingDefaults.length > 0) {
const missingKeys = missingDefaults.map((item) => item.key).join(", ");
logger.error(
`🚨 Aborting preference update: Missing critical default values: ${missingKeys}`
);
return false;
}

return true;
};

// Default preference object
const defaultPreference = {
pollutant: "pm2_5",
Expand All @@ -27,10 +52,22 @@ const defaultPreference = {
unitValue: 14,
unit: "day",
},
airqloud_id: constants.DEFAULT_AIRQLOUD || "NA",
grid_id: constants.DEFAULT_GRID || "NA",
network_id: constants.DEFAULT_NETWORK || "NA",
group_id: constants.DEFAULT_GROUP || "NA",
airqloud_id: constants.DEFAULT_AIRQLOUD,
grid_id: constants.DEFAULT_GRID,
network_id: constants.DEFAULT_NETWORK,
group_id: constants.DEFAULT_GROUP,
};

// Function to validate user's group membership
const validateUserGroupMembership = (user, defaultGroupId) => {
// Check if user has group_roles and is a member of the default group
if (!user.group_roles || user.group_roles.length === 0) {
return false;
}

return user.group_roles.some(
(role) => role.group.toString() === defaultGroupId.toString()
);
};

// Function to get selected sites based on the specified method
Expand Down Expand Up @@ -62,6 +99,11 @@ const getSelectedSites = async (method = "featured") => {
};

const updatePreferences = async (siteSelectionMethod = "featured") => {
// Validate default values before proceeding
if (!validateDefaultValues()) {
return;
}

try {
const batchSize = BATCH_SIZE;
let skip = 0;
Expand All @@ -74,43 +116,60 @@ const updatePreferences = async (siteSelectionMethod = "featured") => {
return;
}

// Use constants.DEFAULT_GROUP directly
const defaultGroupId = mongoose.Types.ObjectId(constants.DEFAULT_GROUP);

while (true) {
// Fetch users with their group_roles
const users = await UserModel("airqo")
.find()
.limit(batchSize)
.skip(skip)
.select("_id")
.select("_id group_roles")
.lean();

if (users.length === 0) {
break;
}

// Fetch existing preferences for users in batch
const userIds = users.map((user) => user._id);
// Filter users who are members of the default group
const validUsers = users.filter((user) =>
validateUserGroupMembership(user, defaultGroupId)
);

// Get user IDs of valid users
const validUserIds = validUsers.map((user) => user._id);

// Fetch existing preferences for valid users
const preferences = await PreferenceModel("airqo")
.find({ user_id: { $in: userIds } })
.find({
user_id: { $in: validUserIds },
group_id: defaultGroupId,
})
.select("_id user_id selected_sites")
.lean();

const preferencesMap = new Map();

preferences.forEach((pref) => {
preferencesMap.set(pref.user_id.toString(), pref);
});

for (const user of users) {
for (const user of validUsers) {
const userIdStr = user._id.toString();
const preference = preferencesMap.get(userIdStr);

// Prepare the default preference object with the specific group_id
const defaultPreferenceWithGroupId = {
...defaultPreference,
user_id: user._id,
group_id: defaultGroupId,
selected_sites: selectedSites,
};

if (!preference) {
// No preference exists, create a new one
// No preference exists for the user in the default group, create a new one
await PreferenceModel("airqo")
.create({
...defaultPreference,
user_id: user._id,
selected_sites: selectedSites,
})
.create(defaultPreferenceWithGroupId)
.catch((error) => {
logger.error(
`🐛🐛 Failed to create preference for user ${userIdStr}: ${stringify(
Expand All @@ -122,14 +181,21 @@ const updatePreferences = async (siteSelectionMethod = "featured") => {
// Preference exists but selected_sites is empty, update it
await PreferenceModel("airqo")
.findOneAndUpdate(
{ _id: preference._id },
{
user_id: user._id,
group_id: defaultGroupId,
},
{
$set: {
...defaultPreference,
selected_sites: selectedSites,
group_id: defaultGroupId,
},
},
{ new: true }
{
new: true,
upsert: true,
setDefaultsOnInsert: true,
}
)
.catch((error) => {
logger.error(
Expand All @@ -154,3 +220,5 @@ cron.schedule(schedule, () => updatePreferences("featured"), {
scheduled: true,
timezone: "Africa/Nairobi",
});

module.exports = { updatePreferences };

0 comments on commit 331742e

Please sign in to comment.