diff --git a/src/entities/project.ts b/src/entities/project.ts index 81aa09209..630b9c425 100644 --- a/src/entities/project.ts +++ b/src/entities/project.ts @@ -76,6 +76,7 @@ export enum SortingField { QualityScore = 'QualityScore', GIVPower = 'GIVPower', InstantBoosting = 'InstantBoosting', + ActiveQfRoundFundsRaised = 'ActiveQfRoundFundsRaised', } export enum FilterField { diff --git a/src/repositories/projectRepository.ts b/src/repositories/projectRepository.ts index 72f7003b4..b01d9322f 100644 --- a/src/repositories/projectRepository.ts +++ b/src/repositories/projectRepository.ts @@ -1,4 +1,4 @@ -import { UpdateResult } from 'typeorm'; +import { QueryBuilder, UpdateResult } from 'typeorm'; import { FilterField, Project, @@ -17,6 +17,8 @@ import { import { User, publicSelectionFields } from '../entities/user'; import { ResourcesTotalPerMonthAndYear } from '../resolvers/donationResolver'; import { OrderDirection, ProjectResolver } from '../resolvers/projectResolver'; +import { findActiveQfRound } from './qfRoundRepository'; +import { Donation } from '../entities/donation'; export const findProjectById = (projectId: number): Promise => { // return Project.findOne({ id: projectId }); @@ -60,7 +62,9 @@ export type FilterProjectQueryInputParams = { sortingBy?: SortingField; qfRoundId?: number; }; -export const filterProjectsQuery = (params: FilterProjectQueryInputParams) => { +export const filterProjectsQuery = async ( + params: FilterProjectQueryInputParams, +) => { const { limit, skip, @@ -115,6 +119,35 @@ export const filterProjectsQuery = (params: FilterProjectQueryInputParams) => { 'projectInstantPower.totalPower', 'projectInstantPower.powerRank', ]); + } else if (sortingBy === SortingField.ActiveQfRoundFundsRaised) { + const activeQfRound = await findActiveQfRound(); + + const subquery = Donation.createQueryBuilder('donation') + .select('donation."projectId", donation."qfRoundId"') + .addSelect('SUM(donation."valueUsd")', 'qfRoundRaisedFunds') + .where(`donation."qfRoundId" = ${activeQfRound?.id || 0}`) + .groupBy('donation."projectId", donation."qfRoundId"') + .getQuery(); + + query = query.leftJoin( + '(' + subquery + ')', + 'donationSummary', + 'project.id = donationSummary."projectId"', + ); + + // query = query + // .leftJoinAndSelect('project.donations', 'donation', 'donation.projectId = project.id AND donation.qfRoundId = :qfRoundId', { qfRoundId: activeQfRound?.id || 0 }) + // // .leftJoin( + // // qb => { + // // return qb + // // .select('donation."projectId", donation."qfRoundId"') + // // .addSelect('SUM(donation."valueUsd")', 'qfRoundRaisedFunds') + // // .from(Donation, 'donation') + // // .groupBy('donation."projectId", donation."qfRoundId"'); + // // }, + // // 'donation', + // // `donation."projectId" = project.id AND donation."qfRoundId" = ${activeQfRound?.id || 0}` + // // ) } // Filters @@ -168,6 +201,9 @@ export const filterProjectsQuery = (params: FilterProjectQueryInputParams) => { 'NULLS LAST', ); break; + case SortingField.ActiveQfRoundFundsRaised: + query.orderBy('donation."qfRoundRaisedFunds"', 'DESC'); + break; default: query .orderBy('projectInstantPower.totalPower', OrderDirection.DESC) diff --git a/src/resolvers/projectResolver.test.ts b/src/resolvers/projectResolver.test.ts index 0aaae662f..a001b28a4 100644 --- a/src/resolvers/projectResolver.test.ts +++ b/src/resolvers/projectResolver.test.ts @@ -597,6 +597,70 @@ function allProjectsTestCases() { ); }); + // it('should return projects, sort by project raised funds in the active QF round DESC', async () => { + // const donor = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); + // const project1 = await saveProjectDirectlyToDb({ + // ...createProjectData(), + // title: String(new Date().getTime()), + // slug: String(new Date().getTime()), + // }); + // const project2 = await saveProjectDirectlyToDb({ + // ...createProjectData(), + // title: String(new Date().getTime()), + // slug: String(new Date().getTime()), + // }); + + // const qfRound = await QfRound.create({ + // isActive: true, + // name: 'test filter by qfRoundId', + // minimumPassportScore: 10, + // allocatedFund: 100, + // beginDate: new Date(), + // endDate: new Date(), + // }).save(); + // project1.qfRounds = [qfRound]; + // await project1.save(); + // project2.qfRounds = [qfRound]; + // await project2.save(); + + // await saveDonationDirectlyToDb( + // { + // ...createDonationData(), + // status: 'verified', + // qfRoundId: qfRound.id, + // valueUsd: 2, + // }, + // donor.id, + // project1.id, + // ); + + // await saveDonationDirectlyToDb( + // { + // ...createDonationData(), + // status: 'verified', + // qfRoundId: qfRound.id, + // valueUsd: 20, + // }, + // donor.id, + // project1.id, + // ); + + // const result = await axios.post(graphqlUrl, { + // query: fetchMultiFilterAllProjectsQuery, + // variables: { + // sortingBy: SortingField.ActiveQfRoundFundsRaised, + // limit: 10, + // }, + // }); + + // assert.equal(result.data.data.allProjects.projects.length, 3); + // // result.data.data.allProjects.projects.forEach(project => { + // // assert.equal(project.qfRounds[0].id, qfRound.id); + // // }); + // qfRound.isActive = false; + // await qfRound.save(); + // }); + it('should return projects, sort by project instant power DESC', async () => { await PowerBoosting.clear(); await InstantPowerBalance.clear(); diff --git a/src/resolvers/projectResolver.ts b/src/resolvers/projectResolver.ts index 2ec7ab8b2..fdc07c84b 100644 --- a/src/resolvers/projectResolver.ts +++ b/src/resolvers/projectResolver.ts @@ -778,7 +778,7 @@ export class ProjectResolver { filterQueryParams.slugArray = campaign.relatedProjectsSlugs; } - const projectsQuery = filterProjectsQuery(filterQueryParams); + const projectsQuery = await filterProjectsQuery(filterQueryParams); const projectsQueryCacheKey = await projectsFiltersThreadPool.queue( hasher => diff --git a/src/services/campaignService.ts b/src/services/campaignService.ts index 1851d13af..f7f646598 100644 --- a/src/services/campaignService.ts +++ b/src/services/campaignService.ts @@ -39,7 +39,7 @@ export const fillCampaignProjects = async (params: { return campaign; } - const projectsQuery = filterProjectsQuery(projectsQueryParams); + const projectsQuery = await filterProjectsQuery(projectsQueryParams); const projectsQueryCacheKey = await projectsFiltersThreadPool.queue(hasher => hasher.hashProjectFilters({ ...projectsQueryParams,