Skip to content

Commit

Permalink
Merge pull request #736 from Giveth/f_update_data_dashboard_queries
Browse files Browse the repository at this point in the history
Add data queries with data arrays by date
  • Loading branch information
CarlosQ96 authored Nov 23, 2022
2 parents df8a187 + f92fe76 commit 2e00dfd
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 55 deletions.
95 changes: 95 additions & 0 deletions src/repositories/donationRepository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Project } from '../entities/project';
import { Donation } from '../entities/donation';
import { ResourcesTotalPerMonthAndYear } from '../resolvers/donationResolver';

export const createDonation = async (data: {
amount: number;
Expand Down Expand Up @@ -85,3 +86,97 @@ export const findUsersWhoDonatedToProject = async (
})
.getRawMany();
};

export const donationsTotalAmountPerDateRange = async (
fromDate?: string,
toDate?: string,
): Promise<number> => {
const query = Donation.createQueryBuilder('donation')
.select(`COALESCE(SUM(donation."valueUsd"), 0)`, 'sum')
.where(`donation.status = 'verified'`);

if (fromDate) {
query.andWhere(`donation."createdAt" >= '${fromDate}'`);
}

if (toDate) {
query.andWhere(`donation."createdAt" <= '${toDate}'`);
}
const donationsUsdAmount = await query.getRawOne();

return donationsUsdAmount.sum;
};

export const donationsTotalAmountPerDateRangeByMonth = async (
fromDate?: string,
toDate?: string,
): Promise<ResourcesTotalPerMonthAndYear[]> => {
const query = Donation.createQueryBuilder('donation')
.select(
`COALESCE(SUM(donation."valueUsd"), 0) AS total, EXTRACT(YEAR from donation."createdAt") as year, EXTRACT(MONTH from donation."createdAt") as month, CONCAT(CAST(EXTRACT(YEAR from donation."createdAt") as VARCHAR), '/', CAST(EXTRACT(MONTH from donation."createdAt") as VARCHAR)) as date`,
)
.where(`donation.status = 'verified'`)
.andWhere('donation."valueUsd" IS NOT NULL');

if (fromDate) {
query.andWhere(`donation."createdAt" >= '${fromDate}'`);
}

if (toDate) {
query.andWhere(`donation."createdAt" <= '${toDate}'`);
}

query.groupBy('year, month');
query.orderBy('year', 'ASC');
query.addOrderBy('month', 'ASC');

return await query.getRawMany();
};

export const donorsCountPerDate = async (
fromDate?: string,
toDate?: string,
): Promise<number> => {
const query = Donation.createQueryBuilder('donation')
.select(
`CAST((COUNT(DISTINCT(donation."userId")) + SUM(CASE WHEN donation."userId" IS NULL THEN 1 ELSE 0 END)) AS int)`,
'count',
)
.where(`donation.status = 'verified'`);

if (fromDate) {
query.andWhere(`donation."createdAt" >= '${fromDate}'`);
}

if (toDate) {
query.andWhere(`donation."createdAt" <= '${toDate}'`);
}

const queryResult = await query.getRawOne();
return queryResult.count;
};

export const donorsCountPerDateByMonthAndYear = async (
fromDate?: string,
toDate?: string,
): Promise<ResourcesTotalPerMonthAndYear[]> => {
const query = Donation.createQueryBuilder('donation')
.select(
`CAST((COUNT(DISTINCT(donation."userId")) + SUM(CASE WHEN donation."userId" IS NULL THEN 1 ELSE 0 END)) AS int) AS total, EXTRACT(YEAR from donation."createdAt") as year, EXTRACT(MONTH from donation."createdAt") as month, CONCAT(CAST(EXTRACT(YEAR from donation."createdAt") as VARCHAR), '/', CAST(EXTRACT(MONTH from donation."createdAt") as VARCHAR)) as date`,
)
.where(`donation.status = 'verified'`);

if (fromDate) {
query.andWhere(`donation."createdAt" >= '${fromDate}'`);
}

if (toDate) {
query.andWhere(`donation."createdAt" <= '${toDate}'`);
}

query.groupBy('year, month');
query.orderBy('year', 'ASC');
query.addOrderBy('month', 'ASC');

return await query.getRawMany();
};
41 changes: 41 additions & 0 deletions src/repositories/projectRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '../utils/errorMessages';
import { Reaction } from '../entities/reaction';
import { publicSelectionFields } from '../entities/user';
import { ResourcesTotalPerMonthAndYear } from '../resolvers/donationResolver';

export const findProjectById = (
projectId: number,
Expand Down Expand Up @@ -134,3 +135,43 @@ export const userIsOwnerOfProject = async (
)
)[0].exists;
};

export const totalProjectsPerDate = async (
fromDate?: string,
toDate?: string,
): Promise<number> => {
const query = Project.createQueryBuilder('project');

if (fromDate) {
query.andWhere(`project."creationDate" >= '${fromDate}'`);
}

if (toDate) {
query.andWhere(`project."creationDate" <= '${toDate}'`);
}

return await query.getCount();
};

export const totalProjectsPerDateByMonthAndYear = async (
fromDate?: string,
toDate?: string,
): Promise<ResourcesTotalPerMonthAndYear[]> => {
const query = Project.createQueryBuilder('project').select(
`COUNT(project.id) as total, EXTRACT(YEAR from project."creationDate") as year, EXTRACT(MONTH from project."creationDate") as month, CONCAT(CAST(EXTRACT(YEAR from project."creationDate") as VARCHAR), '/', CAST(EXTRACT(MONTH from project."creationDate") as VARCHAR)) as date`,
);

if (fromDate) {
query.andWhere(`project."creationDate" >= '${fromDate}'`);
}

if (toDate) {
query.andWhere(`project."creationDate" <= '${toDate}'`);
}

query.groupBy('year, month');
query.orderBy('year', 'ASC');
query.addOrderBy('month', 'ASC');

return query.getRawMany();
};
22 changes: 20 additions & 2 deletions src/resolvers/donationResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,16 @@ function donorsCountPerDateTestCases() {
});
assert.isOk(donationsResponse);
// 1 unique donor and 2 anonymous
assert.equal(donationsResponse.data.data.totalDonorsCountPerDate, 3);
assert.equal(donationsResponse.data.data.totalDonorsCountPerDate.total, 3);
const total =
donationsResponse.data.data.totalDonorsCountPerDate.totalPerMonthAndYear.reduce(
(sum, value) => sum + value.total,
0,
);
assert.equal(
donationsResponse.data.data.totalDonorsCountPerDate.total,
total,
);
});
}

Expand Down Expand Up @@ -185,9 +194,18 @@ function donationsUsdAmountTestCases() {

assert.isOk(donationsResponse);
assert.equal(
donationsResponse.data.data.donationsTotalUsdPerDate,
donationsResponse.data.data.donationsTotalUsdPerDate.total,
donation.valueUsd,
);
const total =
donationsResponse.data.data.donationsTotalUsdPerDate.totalPerMonthAndYear.reduce(
(sum, value) => sum + value.total,
0,
);
assert.equal(
donationsResponse.data.data.donationsTotalUsdPerDate.total,
total,
);
});
}

Expand Down
82 changes: 47 additions & 35 deletions src/resolvers/donationResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ import {
findUserById,
findUserByWalletAddress,
} from '../repositories/userRepository';
import { findDonationById } from '../repositories/donationRepository';
import {
donationsTotalAmountPerDateRange,
donationsTotalAmountPerDateRangeByMonth,
donorsCountPerDate,
donorsCountPerDateByMonthAndYear,
findDonationById,
} from '../repositories/donationRepository';
import { sleep } from '../utils/utils';
import { findProjectRecipientAddressByNetworkId } from '../repositories/projectAddressRepository';
import { MainCategory } from '../entities/mainCategory';
Expand All @@ -71,6 +77,25 @@ class PaginateDonations {
totalEthBalance: number;
}

// As general as posible types to reuse it
@ObjectType()
export class ResourcesTotalPerMonthAndYear {
@Field(type => Number, { nullable: true })
total?: Number;

@Field(type => String, { nullable: true })
date?: String;
}

@ObjectType()
export class ResourcePerDateRange {
@Field(type => Number, { nullable: true })
total?: Number;

@Field(type => [ResourcesTotalPerMonthAndYear], { nullable: true })
totalPerMonthAndYear?: ResourcesTotalPerMonthAndYear[];
}

enum SortDirection {
ASC = 'ASC',
DESC = 'DESC',
Expand Down Expand Up @@ -231,64 +256,51 @@ export class DonationResolver {
}
}

@Query(returns => Number, { nullable: true })
@Query(returns => ResourcePerDateRange, { nullable: true })
async donationsTotalUsdPerDate(
// fromDate and toDate should be in this format YYYYMMDD HH:mm:ss
@Arg('fromDate', { nullable: true }) fromDate?: string,
@Arg('toDate', { nullable: true }) toDate?: string,
): Promise<Number> {
): Promise<ResourcePerDateRange> {
try {
validateWithJoiSchema(
{ fromDate, toDate },
resourcePerDateReportValidator,
);
const query = this.donationRepository
.createQueryBuilder('donation')
.select(`COALESCE(SUM(donation."valueUsd"), 0)`, 'sum')
.where(`donation.status = 'verified'`);

if (fromDate) {
query.andWhere(`donation."createdAt" >= '${fromDate}'`);
}
if (toDate) {
query.andWhere(`donation."createdAt" <= '${toDate}'`);
}
const donationsUsdAmount = await query.getRawOne();

return donationsUsdAmount.sum;
const total = await donationsTotalAmountPerDateRange(fromDate, toDate);
const totalPerMonthAndYear =
await donationsTotalAmountPerDateRangeByMonth(fromDate, toDate);

return {
total,
totalPerMonthAndYear,
};
} catch (e) {
logger.error('donations query error', e);
throw e;
}
}

@Query(returns => Number, { nullable: true })
@Query(returns => ResourcePerDateRange, { nullable: true })
async totalDonorsCountPerDate(
// fromDate and toDate should be in this format YYYYMMDD HH:mm:ss
@Arg('fromDate', { nullable: true }) fromDate?: string,
@Arg('toDate', { nullable: true }) toDate?: string,
): Promise<Number> {
): Promise<ResourcePerDateRange> {
try {
validateWithJoiSchema(
{ fromDate, toDate },
resourcePerDateReportValidator,
);
const query = this.donationRepository
.createQueryBuilder('donation')
.select(
`CAST((COUNT(DISTINCT(donation."userId")) + SUM(CASE WHEN donation."userId" IS NULL THEN 1 ELSE 0 END)) AS int)`,
'count',
)
.where(`donation.status = 'verified'`);

if (fromDate) {
query.andWhere(`donation."createdAt" >= '${fromDate}'`);
}
if (toDate) {
query.andWhere(`donation."createdAt" <= '${toDate}'`);
}
const donors = await query.getRawOne();
return donors.count;
const total = await donorsCountPerDate(fromDate, toDate);
const totalPerMonthAndYear = await donorsCountPerDateByMonthAndYear(
fromDate,
toDate,
);
return {
total,
totalPerMonthAndYear,
};
} catch (e) {
logger.error('donations query error', e);
throw e;
Expand Down
14 changes: 12 additions & 2 deletions src/resolvers/projectResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,26 @@ function projectsPerDateTestCases() {
...createProjectData(),
creationDate: moment().add(10, 'days').toDate(),
});
const project2 = await saveProjectDirectlyToDb({
...createProjectData(),
creationDate: moment().add(44, 'days').toDate(),
});
const projectsResponse = await axios.post(graphqlUrl, {
query: fetchNewProjectsPerDate,
variables: {
fromDate: moment().add(9, 'days').toDate().toISOString().split('T')[0],
toDate: moment().add(11, 'days').toDate().toISOString().split('T')[0],
toDate: moment().add(45, 'days').toDate().toISOString().split('T')[0],
},
});

assert.isOk(projectsResponse);
assert.equal(projectsResponse.data.data.projectsPerDate, 1);
assert.equal(projectsResponse.data.data.projectsPerDate.total, 2);
const total =
projectsResponse.data.data.projectsPerDate.totalPerMonthAndYear.reduce(
(sum, value) => sum + value.total,
0,
);
assert.equal(projectsResponse.data.data.projectsPerDate.total, total);
});
}

Expand Down
Loading

0 comments on commit 2e00dfd

Please sign in to comment.