Untitled

 avatar
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