Skip to content
This repository was archived by the owner on Apr 17, 2025. It is now read-only.

Commit 2fd6423

Browse files
authored
feat: add get-weekly-stats feature (#32)
* refactor: add `GetWeeklyStatsResponse` * refactor: boilerplate `GetWeeklyStatsService` * feat: add `getDistanceAndPriceFromTripsInThisWeek` * refactor: remove dead code * feat: add `StatisticsModule` * docs: add `ApiCreatedResponse` for `GetWeeklyStatsController` * fix: use await in tests
1 parent 380690e commit 2fd6423

File tree

10 files changed

+111
-4
lines changed

10 files changed

+111
-4
lines changed

src/application/app.module.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@ import { PrismaService } from '../infrastructure/prisma/prisma.service'
33
import { GeocodingService } from '../modules/geocoding/geocoding.adapter'
44
import { GeocodeMapsGeocodingService } from '../modules/geocoding/services/geocode-maps/geocode-maps.geocoding'
55
import { TripModule } from '../modules/trips/trip.module'
6+
import { StatisticsModule } from '../modules/statistics/statistics.module'
7+
import { TripRepository } from '../modules/trips/trip.repository'
8+
import { TripMapper } from '../modules/trips/trip.mapper'
69

710
@Module({
8-
imports: [TripModule],
11+
imports: [TripModule, StatisticsModule],
912
controllers: [],
10-
providers: [PrismaService, { provide: GeocodingService, useClass: GeocodeMapsGeocodingService }],
13+
providers: [
14+
PrismaService,
15+
{ provide: GeocodingService, useClass: GeocodeMapsGeocodingService },
16+
TripRepository,
17+
TripMapper,
18+
],
1119
})
1220
export class AppModule {}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Body, Controller, Get, Post } from '@nestjs/common'
2+
3+
import { ApiCreatedResponse } from '@nestjs/swagger'
4+
import { ControllerExecutor } from '../../../../common/controller'
5+
import { GetWeeklyStatsService } from './get-weekly.stats.service'
6+
import { GetWeeklyStatsResponse } from './get-weekly.stats.response'
7+
8+
/** This endpoint logs the trip and automatically calculates the distance between start and destination addresses. */
9+
@Controller()
10+
@ApiCreatedResponse({ description: 'Successfully calculated stats.', type: GetWeeklyStatsResponse })
11+
export class GetWeeklyStatsController extends ControllerExecutor {
12+
constructor(private readonly service: GetWeeklyStatsService) {
13+
super()
14+
}
15+
16+
@Get('/api/stats/weekly')
17+
async executeImplementation(): Promise<GetWeeklyStatsResponse> {
18+
const response = await this.service.execute(undefined)
19+
return response
20+
}
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ApiProperty } from '@nestjs/swagger'
2+
3+
export class GetWeeklyStatsResponse {
4+
@ApiProperty({
5+
description: 'Package price in PLN',
6+
example: '1000.00 PLN',
7+
})
8+
total_price: string
9+
10+
@ApiProperty({
11+
description: 'Distance in format `1.2 km`',
12+
example: '252.17 km',
13+
})
14+
total_distance: string
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { dinero, toDecimal } from 'dinero.js'
2+
import { Usecase } from '../../../../common/domain/usecase/usecase'
3+
import { Distance } from '../../../geocoding/entities/distance'
4+
import { TripRepository } from '../../../trips/trip.repository'
5+
import { GetWeeklyStatsResponse } from './get-weekly.stats.response'
6+
import { PLN } from '@dinero.js/currencies'
7+
import { Injectable } from '@nestjs/common'
8+
9+
@Injectable()
10+
export class GetWeeklyStatsService implements Usecase<undefined, GetWeeklyStatsResponse> {
11+
constructor(private tripRepository: TripRepository) {}
12+
13+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
14+
async execute(request: undefined): Promise<GetWeeklyStatsResponse> {
15+
const { distance, price } = await this.tripRepository.getDistanceAndPriceFromTripsInThisWeek()
16+
17+
return {
18+
total_distance: new Distance(distance).toString(),
19+
total_price: toDecimal(
20+
dinero({ amount: price * 100, currency: PLN }),
21+
({ value, currency }) => `${value} ${currency.code}`,
22+
),
23+
}
24+
}
25+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Module } from '@nestjs/common'
2+
import { GetWeeklyStatsService } from './commands/get-weekly-stats/get-weekly.stats.service'
3+
import { GetWeeklyStatsController } from './commands/get-weekly-stats/get-weekly-stats.controller'
4+
import { TripModule } from '../trips/trip.module'
5+
6+
@Module({
7+
imports: [TripModule],
8+
controllers: [GetWeeklyStatsController],
9+
providers: [GetWeeklyStatsService],
10+
})
11+
export class StatisticsModule {}

src/modules/trips/commands/create-trip/create-trip.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export class CreateTripService implements Usecase<CreateTripRequest, CreateTripR
3636

3737
const formattedPrice = toDecimal(trip.properties.price, ({ value, currency }) => `${value} ${currency.code}`)
3838

39+
console.log(await this.tripRepository.getDistanceAndPriceFromTripsInThisWeek())
40+
3941
return {
4042
id: trip.id,
4143
startAddress: trip.properties.startAddress,

src/modules/trips/commands/create-trip/create-trip.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ describe('CreateTripController', () => {
2828
})
2929

3030
describe('root', () => {
31-
it('should return "Hello World!"', () => {
31+
it('should return "Hello World!"', async () => {
3232
expect(
33-
appController.executeImplementation({
33+
await appController.executeImplementation({
3434
start_address: 'Plac Europejski 2, Warszawa, Polska',
3535
destination_address: 'Kraków, Polska',
3636
price: 1000,

src/modules/trips/trip.module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { PrismaService } from '../../infrastructure/prisma/prisma.service'
1111
@Module({
1212
imports: [],
1313
controllers: [CreateTripController],
14+
exports: [TripMapper, TripRepository],
1415
providers: [
1516
CreateTripService,
1617
TripRepository,

src/modules/trips/trip.repository.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { TripMapper } from './trip.mapper'
33
import { Injectable } from '@nestjs/common'
44
import { Repository } from '../../common/persistance/repository'
55
import { PrismaService } from '../../infrastructure/prisma/prisma.service'
6+
import { Sql } from '@prisma/client/runtime'
7+
import { Prisma } from '@prisma/client'
8+
import { getStartAndEndDateOfCurrentWeek } from '../../utilities/get-week-dates'
69

710
@Injectable()
811
export class TripRepository implements Repository<Trip> {
@@ -39,4 +42,17 @@ export class TripRepository implements Repository<Trip> {
3942

4043
return this.tripMapper.toDomain(place)
4144
}
45+
46+
// TODO: Add prettier formatter for sql
47+
48+
/** Summarise distance and prices from all trips that happened from monday to sunday in current week. */
49+
async getDistanceAndPriceFromTripsInThisWeek(): Promise<{ distance: number; price: number }> {
50+
const { start: monday, end: sunday } = getStartAndEndDateOfCurrentWeek()
51+
52+
const execute = await this.prismaService.$queryRaw<
53+
[{ total_distance: number; total_price: number }]
54+
>`SELECT SUM("distanceInKilometers") AS total_distance, SUM("priceInPLN") AS total_price FROM public."Trip" WHERE "date" > ${monday} ::timestamp AND "date" < ${sunday} ::timestamp`
55+
56+
return { distance: Number(execute[0].total_distance ?? 0), price: Number(execute[0].total_price ?? 0) }
57+
}
4258
}

src/utilities/get-week-dates.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function getStartAndEndDateOfCurrentWeek(): { start: Date; end: Date } {
2+
const currentDate = new Date()
3+
const day = currentDate.getDay()
4+
const diff = currentDate.getDate() - day + (day === 0 ? -6 : 1)
5+
const start = new Date(currentDate.setDate(diff))
6+
const end = new Date(currentDate.setDate(diff + 6))
7+
return { start, end }
8+
}

0 commit comments

Comments
 (0)