Skip to content

Commit

Permalink
campaign statistics endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
tongo-angelov committed Oct 19, 2023
1 parent 0af93ef commit ea85e62
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 0 deletions.
2 changes: 2 additions & 0 deletions apps/api/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { CacheModule } from '@nestjs/cache-manager'
import { CampaignNewsModule } from '../campaign-news/campaign-news.module'
import { CampaignNewsFileModule } from '../campaign-news-file/campaign-news-file.module'
import { MarketingNotificationsModule } from '../notifications/notifications.module'
import { StatisticsModule } from '../statistics/statistics.module'

@Module({
imports: [
Expand Down Expand Up @@ -107,6 +108,7 @@ import { MarketingNotificationsModule } from '../notifications/notifications.mod
JwtModule,
NotificationModule,
BankTransactionsModule,
StatisticsModule,
CacheModule.registerAsync({
imports: [ConfigModule],
useFactory: async (config: ConfigService) => ({
Expand Down
36 changes: 36 additions & 0 deletions apps/api/src/statistics/dto/donation-statistics.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ApiProperty } from '@nestjs/swagger'
import { Expose } from 'class-transformer'

export class GroupedDonationsDto {
@ApiProperty()
@Expose()
sum: number

@ApiProperty()
@Expose()
count: number

@ApiProperty()
@Expose()
date: Date
}

export class UniqueDonationsDto {
@ApiProperty()
@Expose()
amount: number

@ApiProperty()
@Expose()
count: number
}

export class HourlyDonationsDto {
@ApiProperty()
@Expose()
hour: number

@ApiProperty()
@Expose()
count: number
}
5 changes: 5 additions & 0 deletions apps/api/src/statistics/dto/group-by.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum GroupBy {
DAY = 'day',
WEEK = 'week',
MONTH = 'month',
}
38 changes: 38 additions & 0 deletions apps/api/src/statistics/statistics.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Public } from 'nest-keycloak-connect'
import { Controller, Get, Param, Query, UseInterceptors } from '@nestjs/common'
import { CacheInterceptor } from '@nestjs/cache-manager'

import { StatisticsService } from './statistics.service'
import { ApiQuery, ApiTags } from '@nestjs/swagger'
import { GroupBy } from './dto/group-by.dto'

@ApiTags('statistics')
@Controller('statistics')
export class StatisticsController {
constructor(private readonly statisticsService: StatisticsService) {}

@Get('donations/:campaignId')
@UseInterceptors(CacheInterceptor)
@Public()
@ApiQuery({ name: 'groupBy', required: false, enum: GroupBy })
async findGroupedDonations(
@Param('campaignId') campaignId: string,
@Query('groupBy') groupBy?: GroupBy,
) {
return await this.statisticsService.listGroupedDonations(campaignId, groupBy)
}

@Get('unique-donations/:campaignId')
@UseInterceptors(CacheInterceptor)
@Public()
async findUniqueDonations(@Param('campaignId') campaignId: string) {
return await this.statisticsService.listUniqueDonations(campaignId)
}

@Get('hourly-donations/:campaignId')
@UseInterceptors(CacheInterceptor)
@Public()
async findHourlyDonations(@Param('campaignId') campaignId: string) {
return await this.statisticsService.listHourlyDonations(campaignId)
}
}
12 changes: 12 additions & 0 deletions apps/api/src/statistics/statistics.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common'
import { PrismaService } from '../prisma/prisma.service'
import { StatisticsController } from './statistics.controller'
import { StatisticsService } from './statistics.service'

@Module({
controllers: [StatisticsController],
providers: [StatisticsService, PrismaService],

exports: [StatisticsService],
})
export class StatisticsModule {}
59 changes: 59 additions & 0 deletions apps/api/src/statistics/statistics.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Prisma } from '@prisma/client'
import { Injectable } from '@nestjs/common'
import { PrismaService } from '../prisma/prisma.service'

import { GroupBy } from './dto/group-by.dto'
import {
GroupedDonationsDto,
HourlyDonationsDto,
UniqueDonationsDto,
} from './dto/donation-statistics.dto'

@Injectable()
export class StatisticsService {
constructor(private prisma: PrismaService) {}

async listGroupedDonations(
campaignId: string,
groupBy?: GroupBy,
): Promise<GroupedDonationsDto[]> {
const date =
groupBy === GroupBy.MONTH
? Prisma.sql`DATE_TRUNC('MONTH', created_at) date`
: groupBy === GroupBy.WEEK
? Prisma.sql`DATE_TRUNC('WEEK', created_at) date`
: Prisma.sql`DATE_TRUNC('DAY', created_at) date`

const group =
groupBy === GroupBy.MONTH
? Prisma.sql`GROUP BY DATE_TRUNC('MONTH', created_at)`
: groupBy === GroupBy.WEEK
? Prisma.sql`GROUP BY DATE_TRUNC('WEEK', created_at)`
: Prisma.sql`GROUP BY DATE_TRUNC('DAY', created_at)`

return this.prisma.$queryRaw`
SELECT SUM(amount)::INTEGER, COUNT(id)::INTEGER, ${date}
FROM api.donations WHERE status = 'succeeded'
${Prisma.sql`AND target_vault_id IN ( SELECT id from api.vaults WHERE campaign_id = ${campaignId}::uuid)`}
${group}
ORDER BY date ASC `
}

async listUniqueDonations(campaignId: string): Promise<UniqueDonationsDto[]> {
return this.prisma.$queryRaw`
SELECT amount::INTEGER, COUNT(id)::INTEGER AS count
FROM api.donations WHERE status = 'succeeded'
${Prisma.sql`AND target_vault_id IN ( SELECT id from api.vaults WHERE campaign_id = ${campaignId}::uuid)`}
GROUP BY amount
ORDER BY amount ASC`
}

async listHourlyDonations(campaignId: string): Promise<HourlyDonationsDto[]> {
return this.prisma.$queryRaw`
SELECT EXTRACT(HOUR from created_at)::INTEGER AS hour, COUNT(id)::INTEGER AS count
FROM api.donations where status = 'succeeded'
${Prisma.sql`AND target_vault_id IN ( SELECT id from api.vaults WHERE campaign_id = ${campaignId}::uuid)`}
GROUP BY hour
ORDER BY hour ASC`
}
}

0 comments on commit ea85e62

Please sign in to comment.