Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move to production #4079

Merged
merged 12 commits into from
Dec 14, 2024
Merged
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
2 changes: 1 addition & 1 deletion k8s/analytics/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ images:
celeryWorker: eu.gcr.io/airqo-250220/airqo-analytics-celery-worker
reportJob: eu.gcr.io/airqo-250220/airqo-analytics-report-job
devicesSummaryJob: eu.gcr.io/airqo-250220/airqo-analytics-devices-summary-job
tag: prod-74ce6c5b-1734102400
tag: prod-8513ca91-1734151901
api:
name: airqo-analytics-api
label: analytics-api
Expand Down
2 changes: 1 addition & 1 deletion k8s/auth-service/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ app:
replicaCount: 3
image:
repository: eu.gcr.io/airqo-250220/airqo-auth-api
tag: prod-74ce6c5b-1734102400
tag: prod-8513ca91-1734151901
nameOverride: ''
fullnameOverride: ''
podAnnotations: {}
Expand Down
2 changes: 1 addition & 1 deletion k8s/auth-service/values-stage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ app:
replicaCount: 2
image:
repository: eu.gcr.io/airqo-250220/airqo-stage-auth-api
tag: stage-4437d7f9-1734102322
tag: stage-ff5c18e5-1734151846
nameOverride: ''
fullnameOverride: ''
podAnnotations: {}
Expand Down
2 changes: 1 addition & 1 deletion k8s/exceedance/values-prod-airqo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ app:
configmap: env-exceedance-production
image:
repository: eu.gcr.io/airqo-250220/airqo-exceedance-job
tag: prod-74ce6c5b-1734102400
tag: prod-8513ca91-1734151901
nameOverride: ''
fullnameOverride: ''
2 changes: 1 addition & 1 deletion k8s/exceedance/values-prod-kcca.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ app:
configmap: env-exceedance-production
image:
repository: eu.gcr.io/airqo-250220/kcca-exceedance-job
tag: prod-74ce6c5b-1734102400
tag: prod-8513ca91-1734151901
nameOverride: ''
fullnameOverride: ''
2 changes: 1 addition & 1 deletion k8s/predict/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ images:
predictJob: eu.gcr.io/airqo-250220/airqo-predict-job
trainJob: eu.gcr.io/airqo-250220/airqo-train-job
predictPlaces: eu.gcr.io/airqo-250220/airqo-predict-places-air-quality
tag: prod-74ce6c5b-1734102400
tag: prod-8513ca91-1734151901
api:
name: airqo-prediction-api
label: prediction-api
Expand Down
2 changes: 1 addition & 1 deletion k8s/spatial/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ app:
replicaCount: 3
image:
repository: eu.gcr.io/airqo-250220/airqo-spatial-api
tag: prod-74ce6c5b-1734102400
tag: prod-8513ca91-1734151901
nameOverride: ''
fullnameOverride: ''
podAnnotations: {}
Expand Down
2 changes: 1 addition & 1 deletion k8s/website/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ app:
replicaCount: 3
image:
repository: eu.gcr.io/airqo-250220/airqo-website-api
tag: prod-74ce6c5b-1734102400
tag: prod-8513ca91-1734151901
nameOverride: ''
fullnameOverride: ''
podAnnotations: {}
Expand Down
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 };
Loading