diff --git a/src/adapters/cocmAdapter/cocmMockAdapter.ts b/src/adapters/cocmAdapter/cocmMockAdapter.ts index f26ed502f..7f3179a6d 100644 --- a/src/adapters/cocmAdapter/cocmMockAdapter.ts +++ b/src/adapters/cocmAdapter/cocmMockAdapter.ts @@ -1,10 +1,7 @@ -import axios from 'axios'; import { CocmAdapterInterface, ProjectsEstimatedMatchings, } from './cocmAdapterInterface'; -import { i18n, translationErrorMessagesKeys } from '../../utils/errorMessages'; -import { logger } from '../../utils/logger'; export class CocmMockAdapter implements CocmAdapterInterface { async fetchEstimatedClusterMatchings( diff --git a/src/entities/project.ts b/src/entities/project.ts index 8a1a386c0..9a590f4fb 100644 --- a/src/entities/project.ts +++ b/src/entities/project.ts @@ -40,11 +40,7 @@ import { Category } from './category'; import { FeaturedUpdate } from './featuredUpdate'; import { getHtmlTextSummary } from '../utils/utils'; import { QfRound } from './qfRound'; -import { - getQfRoundTotalSqrtRootSumSquared, - getProjectDonationsSqrtRootSum, - findActiveQfRound, -} from '../repositories/qfRoundRepository'; +import { findActiveQfRound } from '../repositories/qfRoundRepository'; import { EstimatedMatching } from '../types/qfTypes'; import { Campaign } from './campaign'; import { ProjectEstimatedMatchingView } from './ProjectEstimatedMatchingView'; @@ -501,23 +497,14 @@ export class Project extends BaseEntity { async estimatedMatching(): Promise { const activeQfRound = await findActiveQfRound(); if (!activeQfRound) { - // TODO should move it to materialized view return null; } - const projectDonationsSqrtRootSum = await getProjectDonationsSqrtRootSum( - this.id, - activeQfRound.id, - ); - - const allProjectsSum = await getQfRoundTotalSqrtRootSumSquared( - activeQfRound.id, - ); - const matchingPool = activeQfRound.allocatedFund; + // Facilitate migration in frontend return empty values for now return { - projectDonationsSqrtRootSum, - allProjectsSum, + projectDonationsSqrtRootSum: 0, + allProjectsSum: 0, matchingPool, }; } diff --git a/src/repositories/donationRepository.ts b/src/repositories/donationRepository.ts index 4b649faaf..1a0908e8c 100644 --- a/src/repositories/donationRepository.ts +++ b/src/repositories/donationRepository.ts @@ -10,6 +10,28 @@ import { ORGANIZATION_LABELS } from '../entities/organization'; import { AppDataSource } from '../orm'; import { getPowerRound } from './powerRoundRepository'; +export const exportClusterMatchingDonationsFormat = async ( + qfRoundId: number, +) => { + return await Donation.query( + ` + SELECT + d."fromWalletAddress" AS voter, + d."toWalletAddress" AS payoutAddress, + d."valueUsd" AS amountUSD, + p."title" AS project_name, + d."qfRoundUserScore" AS score + FROM + donation d + INNER JOIN + project p ON d."projectId" = p."id" + WHERE + d."qfRoundId" = $1 + `, + [qfRoundId], + ); +}; + export const fillQfRoundDonationsUserScores = async (): Promise => { await Donation.query(` UPDATE donation diff --git a/src/server/bootstrap.ts b/src/server/bootstrap.ts index b3509bced..bbe50647c 100644 --- a/src/server/bootstrap.ts +++ b/src/server/bootstrap.ts @@ -56,8 +56,6 @@ import { ApolloContext } from '../types/ApolloContext'; import { ProjectResolverWorker } from '../workers/projectsResolverWorker'; import { runInstantBoostingUpdateCronJob } from '../services/cronJobs/instantBoostingUpdateJob'; -import { refreshProjectEstimatedMatchingView } from '../services/projectViewsService'; -import { isTestEnv } from '../utils/utils'; import { runCheckActiveStatusOfQfRounds } from '../services/cronJobs/checkActiveStatusQfRounds'; import { runUpdateProjectCampaignsCacheJob } from '../services/cronJobs/updateProjectCampaignsCacheJob'; import { corsOptions } from './cors'; diff --git a/src/services/cronJobs/syncEstimatedClusterMatchingJob.ts b/src/services/cronJobs/syncEstimatedClusterMatchingJob.ts index d57c3da17..e23044768 100644 --- a/src/services/cronJobs/syncEstimatedClusterMatchingJob.ts +++ b/src/services/cronJobs/syncEstimatedClusterMatchingJob.ts @@ -2,16 +2,13 @@ import { schedule } from 'node-cron'; import { spawn, Worker, Thread } from 'threads'; import config from '../../config'; import { logger } from '../../utils/logger'; -import { - findActiveQfRound, - findUsersWithoutMBDScoreInActiveAround, -} from '../../repositories/qfRoundRepository'; -import { findUserById } from '../../repositories/userRepository'; -import { UserQfRoundModelScore } from '../../entities/userQfRoundModelScore'; +import { findActiveQfRound } from '../../repositories/qfRoundRepository'; +import { exportClusterMatchingDonationsFormat } from '../../repositories/donationRepository'; const cronJobTime = - (config.get('SYNC_ESTIMATED_CLUSTED_MATCHING_CRONJOB_EXPRESSION') as string) || - '0 * * * * *'; + (config.get( + 'SYNC_ESTIMATED_CLUSTER_MATCHING_CRONJOB_EXPRESSION', + ) as string) || '0 * * * * *'; export const runSyncEstimatedClusterMatchingCronjob = () => { logger.debug( @@ -24,27 +21,33 @@ export const runSyncEstimatedClusterMatchingCronjob = () => { }; export const fetchAndUpdateClusterEstimatedMatching = async () => { - const fetchWorker = await spawn( + const matchingWorker = await spawn( new Worker('../../workers/cocm/fetchEstimatedClusterMtchingWorker'), ); - const updateWorker = await spawn( - new Worker('../../workers/cocm/updateProjectsEstimatedClusterMatchingWorker') + const activeQfRound = await findActiveQfRound(); + if (!activeQfRound?.id) return; + + const clusterMatchingDonations = await exportClusterMatchingDonationsFormat( + activeQfRound?.id, ); - const activeQfRoundId = - (await findActiveQfRound())?.id; - if (!activeQfRoundId || activeQfRoundId === 0) return; - - for (const projectId of []) { - try { - - // const userScore = await worker.syncUserScore({ - // userWallet: user?.walletAddress, - // }); - } catch (e) { - logger.info(`User with Id ${1} did not sync MBD score this batch`); - } - } - await Thread.terminate(fetchWorker); - await Thread.terminate(updateWorker); + if (clusterMatchingDonations?.length === 0) return; + + const matchingDataInput = { + votes_data: clusterMatchingDonations, + strategy: 'COCM', + min_donation_threshold_amount: activeQfRound.minimumValidUsdValue, + matching_cap_amount: activeQfRound.maximumReward, + matching_amount: activeQfRound.allocatedFundUSD, + passport_threshold: activeQfRound.minimumPassportScore, + }; + + const matchingData = + await matchingWorker.fetchEstimatedClusterMatching(matchingDataInput); + await matchingWorker.updateEstimatedClusterMatching( + activeQfRound.id, + matchingData, + ); + + await Thread.terminate(matchingWorker); }; diff --git a/src/workers/cocm/estimatedClusterMatchingWorker.ts b/src/workers/cocm/estimatedClusterMatchingWorker.ts new file mode 100644 index 000000000..c3e4ac5a8 --- /dev/null +++ b/src/workers/cocm/estimatedClusterMatchingWorker.ts @@ -0,0 +1,55 @@ +// workers/auth.js +import { expose } from 'threads/worker'; +import { WorkerModule } from 'threads/dist/types/worker'; +import { getClusterMatchingAdapter } from '../../adapters/adaptersFactory'; +import { EstimatedClusterMatching } from '../../entities/estimatedClusterMatching'; +import { logger } from '../../utils/logger'; + +type EstimatedClusterMatchingWorkerFunctions = + | 'fetchEstimatedClusterMatching' + | 'updateEstimatedClusterMatching'; + +export type EstimatedClusterMatchingWorker = + WorkerModule; + +const worker: EstimatedClusterMatchingWorker = { + async fetchEstimatedClusterMatching(matchingDataInput: any) { + return await getClusterMatchingAdapter().fetchEstimatedClusterMatchings( + matchingDataInput, + ); + }, + + async updateEstimatedClusterMatching(qfRoundId: number, matchingData: any) { + try { + // Prepare values for bulk insert + const values = matchingData + .map( + data => `( + (SELECT id FROM project WHERE title = '${data.project_name}'), + ${qfRoundId}, + ${data.matching_amount} + )`, + ) + .join(','); + + const query = ` + INSERT INTO estimated_cluster_matching ("projectId", "qfRoundId", matching) + VALUES ${values} + ON CONFLICT ("projectId", "qfRoundId") + DO UPDATE SET matching = EXCLUDED.matching + RETURNING "projectId", "qfRoundId", matching; + `; + + const result = await EstimatedClusterMatching.query(query); + if (result.length === 0) { + throw new Error('No records were inserted or updated.'); + } + + logger.debug('Matching data processed successfully with raw SQL.'); + } catch (error) { + logger.debug('Error processing matching data:', error.message); + } + }, +}; + +expose(worker); diff --git a/src/workers/cocm/fetchEstimatedClusterMatchingWorker.ts b/src/workers/cocm/fetchEstimatedClusterMatchingWorker.ts deleted file mode 100644 index 37daeeaaf..000000000 --- a/src/workers/cocm/fetchEstimatedClusterMatchingWorker.ts +++ /dev/null @@ -1,17 +0,0 @@ -// workers/auth.js -import { expose } from 'threads/worker'; -import { WorkerModule } from 'threads/dist/types/worker'; -import { getClusterMatchingAdapter } from '../../adapters/adaptersFactory'; - -type FetchEstimatedClusterMatchingWorkerFunctions = 'fetchEstimatedClusterMatching'; - -export type FetchEstimatedClusterMatchingWorker = - WorkerModule; - -const worker: FetchEstimatedClusterMatchingWorker = { - async fetchEstimatedClusterMatching(matchingDataInput: any) { - return await getClusterMatchingAdapter().fetchEstimatedClusterMatchings(matchingDataInput); - }, -}; - -expose(worker); diff --git a/src/workers/cocm/updateProjectsEstimatedClusterMatchingWorker.ts b/src/workers/cocm/updateProjectsEstimatedClusterMatchingWorker.ts deleted file mode 100644 index e69de29bb..000000000