Untitled
unknown
typescript
a year ago
8.0 kB
5
Indexable
exports.dailyUserScoreCalculation = functions.pubsub.schedule('55 23 * * *').timeZone('Asia/Taipei').onRun(async (context) => { // Fetch all existing users and store their IDs in a string array const usersSnapshot = await admin.firestore().collection('Users').get(); const userIds = usersSnapshot.docs.map(doc => doc.id); // Iterate over users to calculate scores for (let i = 0; i < userIds.length; i++) { for (let j = i + 1; j < userIds.length; j++) { // Check if a score entry already exists for this pair const scoreEntrySnapshot = await admin.firestore().collection('scores') .where('user1', 'in', [userIds[i], userIds[j]]) .where('user2', 'in', [userIds[i], userIds[j]]) .get(); if (scoreEntrySnapshot.empty) { // No entry exists, calculate score const scoreResult = await calculateMatchScores(userIds[i], userIds[j]); // Proceed only if score is greater than 0 if (scoreResult.score > 0) { // Add score entry to Firestore await admin.firestore().collection('scores').add({ user1: userIds[i], user2: userIds[j], score: scoreResult.score }); } } } } console.log('Daily user score calculation completed for non-zero scores'); }); async function calculateMatchScores(userIdA: string, userIdB: string): Promise<{ score: number }> { const db = admin.firestore(); const userADoc = await db.collection('Users').doc(userIdA).get(); const userBDoc = await db.collection('Users').doc(userIdB).get(); const userA = userADoc.data(); const userB = userBDoc.data(); if (!userA || !userB || !userA.favoriteArtists || !userB.favoriteArtists || !userA.similarArtists || !userB.similarArtists) { console.log(`Incomplete data for users ${userIdA} and ${userIdB}. Returning default score of 0.`); return { score: 0 }; } let score = 0; const userASelectedArtistNames: string[] = userA.favoriteArtists.map((artist: Artist) => artist.name); const userBSelectedArtistNames: string[] = userB.favoriteArtists.map((artist: Artist) => artist.name); const sharedSelectedArtists: string[] = userASelectedArtistNames.filter((artistNameA: string) => userBSelectedArtistNames.includes(artistNameA)); score += sharedSelectedArtists.length * 9; const sharedSelectedSimilarArtistsAB: string[] = userASelectedArtistNames.filter((artistNameA: string) => userB.similarArtists.includes(artistNameA)); score += sharedSelectedSimilarArtistsAB.length * 3; const sharedSelectedSimilarArtistsBA: string[] = userBSelectedArtistNames.filter((artistNameB: string) => userA.similarArtists.includes(artistNameB)); score += sharedSelectedSimilarArtistsBA.length * 3; const sharedSimilarArtists: string[] = userA.similarArtists.filter((similarArtist: string) => userB.similarArtists.includes(similarArtist)); score += sharedSimilarArtists.length; console.log("Score:", score); return { score }; } exports.fetchPotentialProfilesWithScoresNew = functions.runWith(runtimeOpts).pubsub.schedule('40 23 * * *') .timeZone('Asia/Taipei') .onRun(async (context) => { console.log("Starting to assign daily profiles based on scores..."); const db = admin.firestore(); const usersSnapshot = await db.collection('Users').get(); let users = usersSnapshot.docs.map(doc => doc.id).filter(id => !["demo1", "demo2", "demo3"].includes(id)); users = shuffleArray(users); for (let userID of users) { // Check if the current user already has 3 matches for tomorrow const alreadyHasThreeMatches = await canBeShownTomorrow(userID); if (!alreadyHasThreeMatches) { console.log(`User ${userID} already has 3 or more profiles assigned for tomorrow.`); continue; // Skip to the next user } let potentialProfiles: PotentialProfiles = {}; // Use an interface or type for PotentialProfiles for (let potentialMatchID of users) { if (userID === potentialMatchID) continue; // Skip self const pairId = [userID, potentialMatchID].sort().join('_'); const scoreDoc = await db.collection('newScores').doc(pairId).get(); if (scoreDoc.exists && scoreDoc.data()?.score > 0) { potentialProfiles[potentialMatchID] = scoreDoc.data()!.score; } } let sortedPotentialProfiles = Object.entries(potentialProfiles) .map(([id, score]) => ({ id, score })) .sort((a, b) => b.score - a.score); let assignedProfiles = []; for (let profile of sortedPotentialProfiles) { if (assignedProfiles.length >= 3) break; const canShow = await canBeShownTomorrow(userID) && await canBeShownTomorrow(profile.id); const notShownBefore = !(await hasBeenShown(userID, profile.id)) && !(await hasBeenShown(profile.id, userID)); if (canShow && notShownBefore) { assignedProfiles.push(profile.id); await addToShownProfilesNew(userID, profile.id); await addToShownProfilesNew(profile.id, userID); } } //await sendNotificationAboutNewProfiles(userID); } console.log("Finished assigning daily profiles based on scores."); }); async function addToShownProfilesNew(userID: string, profileID: string): Promise<void> { const interactionsRef = db.collection('Interactions').doc(userID); const formattedTomorrow = getTomorrowInTaipei(); // Get formatted tomorrow's date in Taipei's timezone const doc = await interactionsRef.get(); let shownProfiles = doc.exists ? doc.data()?.shownProfiles ?? {} : {}; if (!shownProfiles[formattedTomorrow]) shownProfiles[formattedTomorrow] = []; shownProfiles[formattedTomorrow].push(profileID); console.log(`Added profile for ${formattedTomorrow}: ${profileID}`); await interactionsRef.set({ shownProfiles }, { merge: true }); } async function canBeShownTomorrow(userID: string): Promise<boolean> { const formattedTomorrow = getTomorrowInTaipei(); // Get formatted tomorrow's date in Taipei's timezone const interactionsRef = db.collection('Interactions').doc(userID); const doc = await interactionsRef.get(); if (!doc.exists) { // If the document doesn't exist, the user hasn't been shown any profiles for tomorrow, so they can be shown. return true; } const data = doc.data(); if (!data?.shownProfiles || !data?.shownProfiles[formattedTomorrow]) { // If there's no entry for tomorrow, the user can be shown profiles. return true; } // If there are fewer than 3 entries for tomorrow, the user can be shown more profiles. return data.shownProfiles[formattedTomorrow].length < 3; } async function hasBeenShown(userID: string, profileID: string): Promise<boolean> { const interactionsRef = db.collection('Interactions').doc(userID); const doc = await interactionsRef.get(); if (!doc.exists || !doc.data()) return false; const data = doc.data()!; // Assume shownProfiles is an object where each key is a string and each value is an array of strings // This assertion helps TypeScript understand the structure we expect const shownProfiles: {[key: string]: string[]} = data.shownProfiles || {}; return Object.values(shownProfiles).some((profiles: string[]) => profiles.includes(profileID)); }
Editor is loading...
Leave a Comment