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

Commit aad6e0c

Browse files
authored
feat: add get-monthly-stats feature (#34)
* feat: add `GetMonthlyStatsService` * test: adjust test to new distance format * test: adjust test to new distance format * test: add `CreateTripController` e2e boilerplate * test: add `create-trip` e2e * test: add `get-monthly-stats` e2e * test: add `get-weekly-stats` e2e * ci: run e2e tests in pipeline * test: describe `getStartAndEndDateOfCurrentWeek` * test: improve coverage config
1 parent 2fd6423 commit aad6e0c

File tree

18 files changed

+277
-46
lines changed

18 files changed

+277
-46
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ jobs:
6262
curl -Os https://uploader.codecov.io/latest/linux/codecov && chmod +x codecov && ./codecov -t ${{
6363
secrets.CODECOV_TOKEN }} -n "GitHub Actions" -F unit
6464

65-
# - name: Test E2E
66-
# run: yarn test:e2e
65+
- name: Test E2E
66+
run: yarn test:e2e
6767

6868
build:
6969
runs-on: ubuntu-latest

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ available PostgresSql database, additionally your machine should contain the sof
6262
cp example.env .env
6363
yarn install
6464
yarn build
65+
yarn db:push
6566
node dist/main.js
6667
# Voila! Application is running at http://localhost:1337
6768
```

jest.config.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { Config } from 'jest'
2+
3+
const config: Config = {
4+
verbose: true,
5+
moduleFileExtensions: ['js', 'json', 'ts'],
6+
rootDir: 'src',
7+
testRegex: '.*\\.spec\\.ts$',
8+
transform: {
9+
'^.+\\.(t|j)s$': 'ts-jest',
10+
},
11+
collectCoverageFrom: [
12+
'**/*.(t|j)s',
13+
// Do not expect coverage from modules, controllers and responses
14+
'!**/*.module.(t|j)s',
15+
'!**/*.response.(t|j)s',
16+
'!**/*.controller.(t|j)s',
17+
// Do not expect coverage from main.ts
18+
'!**/main.(t|j)s',
19+
// Do not expect coverage from prisma service
20+
'!**/prisma.service.(t|j)s',
21+
// Do not expect coverage from common files as they are just generics
22+
'!**/common/*.ts',
23+
],
24+
coveragePathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/test/', '<rootDir>/dist/'],
25+
coverageDirectory: '../coverage',
26+
coverageReporters: ['clover', 'json', 'lcov', 'text-summary', 'html'],
27+
testEnvironment: 'node',
28+
}
29+
30+
export default config

package.json

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
{
22
"name": "bikeramp",
33
"version": "0.0.1",
4-
"description": "",
5-
"author": "",
64
"private": true,
5+
"description": "",
76
"license": "UNLICENSED",
7+
"author": "",
88
"scripts": {
99
"build": "nest build",
10+
"db:generate": "prisma generate",
11+
"db:migrate:deploy": "prisma migrate deploy",
12+
"db:migrate:dev": "prisma migrate dev",
13+
"db:push": "prisma db push",
14+
"db:push:force": "prisma db push --accept-data-loss",
15+
"db:studio": "prisma studio",
1016
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
17+
"postinstall": "prisma generate",
18+
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
1119
"start": "nest start",
12-
"start:dev": "nest start --watch",
1320
"start:debug": "nest start --debug --watch",
21+
"start:dev": "nest start --watch",
1422
"start:prod": "node dist/main",
15-
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
1623
"test": "jest",
17-
"test:watch": "jest --watch",
1824
"test:cov": "jest --coverage",
1925
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
2026
"test:e2e": "jest --config ./test/jest-e2e.json",
21-
"db:generate": "prisma generate",
22-
"db:migrate:deploy": "prisma migrate deploy",
23-
"db:migrate:dev": "prisma migrate dev",
24-
"db:push": "prisma db push",
25-
"db:push:force": "prisma db push --accept-data-loss",
26-
"db:studio": "prisma studio",
27-
"postinstall": "prisma generate"
27+
"test:watch": "jest --watch"
2828
},
2929
"dependencies": {
3030
"@dinero.js/currencies": "^2.0.0-alpha.13",
@@ -69,23 +69,6 @@
6969
"tsconfig-paths": "4.1.1",
7070
"typescript": "^4.7.4"
7171
},
72-
"jest": {
73-
"moduleFileExtensions": [
74-
"js",
75-
"json",
76-
"ts"
77-
],
78-
"rootDir": "src",
79-
"testRegex": ".*\\.spec\\.ts$",
80-
"transform": {
81-
"^.+\\.(t|j)s$": "ts-jest"
82-
},
83-
"collectCoverageFrom": [
84-
"**/*.(t|j)s"
85-
],
86-
"coverageDirectory": "../coverage",
87-
"testEnvironment": "node"
88-
},
8972
"volta": {
9073
"node": "18.13.0",
9174
"yarn": "1.19.2"

src/modules/geocoding/entities/distance.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ describe('Distance', () => {
1313

1414
it('should return distance in kilometers', () => {
1515
const distance = Distance.fromMeters(1000)
16-
expect(distance.toString()).toBe('1.00 km')
16+
expect(distance.toString()).toBe('1km')
1717
})
1818
})

src/modules/geocoding/entities/distance.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export class Distance {
1010
}
1111

1212
toString() {
13-
return `${this.baseScalar.toFixed(2)} km`
13+
return `${this.baseScalar.toFixed(0)}km`
1414
}
1515

1616
toMeters() {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Controller, Get } from '@nestjs/common'
2+
3+
import { ApiCreatedResponse } from '@nestjs/swagger'
4+
import { ControllerExecutor } from '../../../../common/controller'
5+
import { GetMonthlyStatsService } from './get-monthly-stats.service'
6+
import { GetMonthlyStatsResponse } from './get-monthly-stats.response'
7+
8+
@Controller()
9+
export class GetMonthlyStatsController extends ControllerExecutor {
10+
constructor(private readonly service: GetMonthlyStatsService) {
11+
super()
12+
}
13+
14+
@Get('/api/stats/monthly')
15+
@ApiCreatedResponse({
16+
description: 'Get monthly stats',
17+
isArray: true,
18+
type: GetMonthlyStatsResponse,
19+
})
20+
async executeImplementation(): Promise<GetMonthlyStatsResponse[]> {
21+
const response = await this.service.execute()
22+
return response
23+
}
24+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ApiProperty } from '@nestjs/swagger'
2+
3+
export class GetMonthlyStatsResponse {
4+
@ApiProperty({
5+
example: 'July, 4th',
6+
})
7+
day: string
8+
9+
@ApiProperty({
10+
description: 'Distance in format `1.2 km`',
11+
example: '252.17 km',
12+
})
13+
total_distance: string
14+
15+
@ApiProperty({
16+
description: 'Average ride distance',
17+
example: '1.22 km',
18+
})
19+
avg_ride: string
20+
21+
@ApiProperty({
22+
description: 'Average price.',
23+
example: '1000.00 PLN',
24+
})
25+
avg_price: string
26+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 { PLN } from '@dinero.js/currencies'
6+
import { Injectable } from '@nestjs/common'
7+
import { GetMonthlyStatsResponse } from './get-monthly-stats.response'
8+
import { Formatter } from '../../../../utilities/formatter'
9+
10+
@Injectable()
11+
export class GetMonthlyStatsService implements Usecase<undefined, GetMonthlyStatsResponse[]> {
12+
constructor(private tripRepository: TripRepository) {}
13+
14+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
15+
async execute(): Promise<GetMonthlyStatsResponse[]> {
16+
const summarised = await this.tripRepository.getDistanceAndAveragePriceAndAverageDistanceFromTripsInThisMonth()
17+
18+
const response: GetMonthlyStatsResponse[] = []
19+
20+
summarised.map((summarised) => {
21+
response.push({
22+
day: Formatter.formatDateToMonthAndDayOfMonthFormat(summarised.day),
23+
total_distance: Formatter.formatDistance(summarised.totalDistance),
24+
avg_ride: Formatter.formatDistance(summarised.averageDistance),
25+
avg_price: Formatter.formatFiat(summarised.averagePrice),
26+
})
27+
})
28+
29+
return response
30+
}
31+
}

src/modules/statistics/statistics.module.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { Module } from '@nestjs/common'
22
import { GetWeeklyStatsService } from './commands/get-weekly-stats/get-weekly.stats.service'
33
import { GetWeeklyStatsController } from './commands/get-weekly-stats/get-weekly-stats.controller'
44
import { TripModule } from '../trips/trip.module'
5+
import { GetMonthlyStatsController } from './commands/get-monthly-stats/get-monthly-stats.controller'
6+
import { GetMonthlyStatsService } from './commands/get-monthly-stats/get-monthly-stats.service'
57

68
@Module({
79
imports: [TripModule],
8-
controllers: [GetWeeklyStatsController],
9-
providers: [GetWeeklyStatsService],
10+
controllers: [GetWeeklyStatsController, GetMonthlyStatsController],
11+
providers: [GetWeeklyStatsService, GetMonthlyStatsService],
1012
})
1113
export class StatisticsModule {}

0 commit comments

Comments
 (0)