Compare commits
No commits in common. "11ab6f66f74b77e83fbd41e891503cedc0407661" and "e052b06c834478d8ca5c98b1959fd9a7c9335b08" have entirely different histories.
11ab6f66f7
...
e052b06c83
@ -1,8 +0,0 @@
|
|||||||
/*
|
|
||||||
Warnings:
|
|
||||||
|
|
||||||
- You are about to drop the column `geometry` on the `TrainTrip` table. All the data in the column will be lost.
|
|
||||||
|
|
||||||
*/
|
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "TrainTrip" DROP COLUMN "geometry";
|
|
@ -65,6 +65,7 @@ model TrainTrip {
|
|||||||
departureTime DateTime @db.Timestamptz(3)
|
departureTime DateTime @db.Timestamptz(3)
|
||||||
arrivalTime DateTime @db.Timestamptz(3)
|
arrivalTime DateTime @db.Timestamptz(3)
|
||||||
infoJson Json
|
infoJson Json
|
||||||
|
geometry String
|
||||||
moneyUpdate MoneyUpdate?
|
moneyUpdate MoneyUpdate?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
const EARTH_RADIUS = 6378137
|
|
||||||
|
|
||||||
type Coordinates = {
|
|
||||||
latitude: number
|
|
||||||
longitude: number
|
|
||||||
}
|
|
||||||
|
|
||||||
type UseDistanceTypes = {
|
|
||||||
from: Coordinates
|
|
||||||
to: Coordinates
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toRadians(degrees: number) {
|
|
||||||
return (degrees * Math.PI) / 180
|
|
||||||
}
|
|
||||||
|
|
||||||
export function distanceCoordinates({ from, to }: UseDistanceTypes) {
|
|
||||||
const distance = EARTH_RADIUS * Math.acos(
|
|
||||||
Math.sin(toRadians(to.latitude)) * Math.sin(toRadians(from.latitude)) +
|
|
||||||
Math.cos(toRadians(to.latitude)) * Math.cos(toRadians(from.latitude)) * Math.cos(toRadians(from.longitude) - toRadians(to.longitude)),
|
|
||||||
)
|
|
||||||
return distance
|
|
||||||
}
|
|
@ -1,10 +1,10 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger"
|
import { ApiProperty } from "@nestjs/swagger"
|
||||||
import { JsonValue } from "@prisma/client/runtime/library"
|
import { JsonValue } from "@prisma/client/runtime/library"
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import { IsDate, IsInt, IsJSON, IsNumber, IsString, IsUUID } from "class-validator"
|
import { IsDate, IsInt, IsJSON, IsNumber, IsString } from "class-validator"
|
||||||
|
|
||||||
export class CreateTrainDto {
|
export class CreateTrainDto {
|
||||||
@IsUUID()
|
@IsString()
|
||||||
@ApiProperty({ description: "Identifiant du train, donné par l'identifiant de partage Interrail" })
|
@ApiProperty({ description: "Identifiant du train, donné par l'identifiant de partage Interrail" })
|
||||||
id: string
|
id: string
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ export class CreateTrainDto {
|
|||||||
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
@ApiProperty({ description: "Distance estimée en mètres du trajet" })
|
@ApiProperty({ description: "Distance en mètres du trajet, calculé sur https://signal.eu.org/osm/" })
|
||||||
distance: number
|
distance: number
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@ -39,4 +39,8 @@ export class CreateTrainDto {
|
|||||||
@IsJSON()
|
@IsJSON()
|
||||||
@ApiProperty({ description: "Informations JSON supplémentaires transmises par Interrail" })
|
@ApiProperty({ description: "Informations JSON supplémentaires transmises par Interrail" })
|
||||||
infoJson: JsonValue
|
infoJson: JsonValue
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@ApiProperty({ description: "Géométrie de la course, obtenue par https://signal.eu.org/osm/" })
|
||||||
|
geometry: string
|
||||||
}
|
}
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger"
|
|
||||||
import { IsUUID } from "class-validator"
|
|
||||||
|
|
||||||
export class ImportTrainDto {
|
|
||||||
@IsUUID()
|
|
||||||
@ApiProperty({ description: "Identifiant de partage Interrail" })
|
|
||||||
id: string
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
export interface InterrailLeg {
|
|
||||||
infoJson: string
|
|
||||||
sortOrder: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailTravel {
|
|
||||||
date: string
|
|
||||||
infoJson: string
|
|
||||||
from: string
|
|
||||||
to: string
|
|
||||||
type: number
|
|
||||||
legs: InterrailLeg[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailJourneyData {
|
|
||||||
travels: InterrailTravel[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailJourney {
|
|
||||||
data: InterrailJourneyData
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailTime {
|
|
||||||
hours: number
|
|
||||||
minutes: number
|
|
||||||
offset: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailDate {
|
|
||||||
day: number
|
|
||||||
month: number
|
|
||||||
year: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailTravelInfo {
|
|
||||||
arrivalTime: InterrailTime
|
|
||||||
date: InterrailDate
|
|
||||||
departureTime: InterrailTime
|
|
||||||
haconVersion: number
|
|
||||||
dataSource: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailStopExtraInfo {
|
|
||||||
departureTime: InterrailTime
|
|
||||||
index: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailStopCoordinates {
|
|
||||||
latitude: number
|
|
||||||
longitude: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailStopStation {
|
|
||||||
coordinates: InterrailStopCoordinates
|
|
||||||
country: string
|
|
||||||
name: string
|
|
||||||
stationId: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailLegInfo {
|
|
||||||
attributeCodes: string[]
|
|
||||||
attributes: object
|
|
||||||
duration: InterrailTime
|
|
||||||
directionStation: string
|
|
||||||
endTime: InterrailTime
|
|
||||||
isSeparateTicket: boolean
|
|
||||||
operationDays: string
|
|
||||||
operator: object
|
|
||||||
dataSource: number
|
|
||||||
startTime: InterrailTime
|
|
||||||
stopExtraInfo: InterrailStopExtraInfo[]
|
|
||||||
trainName: string
|
|
||||||
trainStopStations: InterrailStopStation[]
|
|
||||||
trainType: number
|
|
||||||
}
|
|
@ -13,7 +13,7 @@ export class TrainEntity implements TrainTrip {
|
|||||||
@ApiProperty({ description: "Identifiant de l'utilisateur⋅rice effectuant le trajet" })
|
@ApiProperty({ description: "Identifiant de l'utilisateur⋅rice effectuant le trajet" })
|
||||||
userId: number
|
userId: number
|
||||||
|
|
||||||
@ApiProperty({ description: "Distance estimée en mètres du trajet" })
|
@ApiProperty({ description: "Distance en mètres du trajet, calculé sur https://signal.eu.org/osm/" })
|
||||||
distance: number
|
distance: number
|
||||||
|
|
||||||
@ApiProperty({ description: "Nom de la gare de départ" })
|
@ApiProperty({ description: "Nom de la gare de départ" })
|
||||||
@ -30,4 +30,7 @@ export class TrainEntity implements TrainTrip {
|
|||||||
|
|
||||||
@ApiProperty({ description: "Informations JSON supplémentaires transmises par Interrail" })
|
@ApiProperty({ description: "Informations JSON supplémentaires transmises par Interrail" })
|
||||||
infoJson: JsonValue
|
infoJson: JsonValue
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Géométrie de la course, obtenue par https://signal.eu.org/osm/" })
|
||||||
|
geometry: string
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import { Controller, Get, Post, Body, Patch, Param, Delete, HttpCode, UseGuards, Query, ParseIntPipe, NotFoundException, Req } from '@nestjs/common'
|
import { Controller, Get, Post, Body, Patch, Param, Delete, HttpCode, UseGuards, Query, ParseIntPipe, NotFoundException } from '@nestjs/common'
|
||||||
import { TrainsService } from './trains.service'
|
import { TrainsService } from './trains.service'
|
||||||
import { CreateTrainDto } from './dto/create-train.dto'
|
import { CreateTrainDto } from './dto/create-train.dto'
|
||||||
import { UpdateTrainDto } from './dto/update-train.dto'
|
import { UpdateTrainDto } from './dto/update-train.dto'
|
||||||
import { TrainEntity } from './entities/train.entity'
|
import { TrainEntity } from './entities/train.entity'
|
||||||
import { AuthenticatedRequest, JwtAuthGuard } from 'src/auth/jwt-auth.guard'
|
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard'
|
||||||
import { ApiBearerAuth, ApiCreatedResponse, ApiForbiddenResponse, ApiNotFoundResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger'
|
import { ApiBearerAuth, ApiCreatedResponse, ApiForbiddenResponse, ApiNotFoundResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger'
|
||||||
import { ApiOkResponsePaginated, paginateOutput } from 'src/common/utils/pagination.utils'
|
import { ApiOkResponsePaginated, paginateOutput } from 'src/common/utils/pagination.utils'
|
||||||
import { QueryPaginationDto } from 'src/common/dto/pagination-query.dto'
|
import { QueryPaginationDto } from 'src/common/dto/pagination-query.dto'
|
||||||
import { PaginateOutputDto } from 'src/common/dto/pagination-output.dto'
|
import { PaginateOutputDto } from 'src/common/dto/pagination-output.dto'
|
||||||
import { ImportTrainDto } from './dto/import-train.dto'
|
|
||||||
|
|
||||||
@Controller('trains')
|
@Controller('trains')
|
||||||
export class TrainsController {
|
export class TrainsController {
|
||||||
@ -73,16 +72,4 @@ export class TrainsController {
|
|||||||
async remove(@Param('id') id: string) {
|
async remove(@Param('id') id: string) {
|
||||||
await this.trainsService.remove(id)
|
await this.trainsService.remove(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("/import")
|
|
||||||
@HttpCode(201)
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@ApiCreatedResponse({ type: TrainEntity, description: "Train importé avec succès" })
|
|
||||||
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
|
|
||||||
@ApiForbiddenResponse({ description: "Permission refusée" })
|
|
||||||
async import(@Req() request: AuthenticatedRequest, @Body() importTrainDto: ImportTrainDto): Promise<TrainEntity> {
|
|
||||||
const train = await this.trainsService.import(request.user, importTrainDto)
|
|
||||||
return new TrainEntity(train)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import { Injectable, NotAcceptableException } from '@nestjs/common'
|
import { Injectable } from '@nestjs/common'
|
||||||
import { CreateTrainDto } from './dto/create-train.dto'
|
import { CreateTrainDto } from './dto/create-train.dto'
|
||||||
import { UpdateTrainDto } from './dto/update-train.dto'
|
import { UpdateTrainDto } from './dto/update-train.dto'
|
||||||
import { PrismaService } from 'src/prisma/prisma.service'
|
import { PrismaService } from 'src/prisma/prisma.service'
|
||||||
import { TrainTrip, User } from '@prisma/client'
|
import { TrainTrip } from '@prisma/client'
|
||||||
import { QueryPaginationDto } from 'src/common/dto/pagination-query.dto'
|
import { QueryPaginationDto } from 'src/common/dto/pagination-query.dto'
|
||||||
import { paginate } from 'src/common/utils/pagination.utils'
|
import { paginate } from 'src/common/utils/pagination.utils'
|
||||||
import { ImportTrainDto } from './dto/import-train.dto'
|
|
||||||
import { InterrailJourney, InterrailLegInfo, InterrailTravelInfo } from './dto/interrail-api.dto'
|
|
||||||
import { distanceCoordinates } from 'src/common/utils/calculus.utils'
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TrainsService {
|
export class TrainsService {
|
||||||
@ -22,7 +19,7 @@ export class TrainsService {
|
|||||||
await this.prisma.trainTrip.findMany({
|
await this.prisma.trainTrip.findMany({
|
||||||
...paginate(queryPagination),
|
...paginate(queryPagination),
|
||||||
}),
|
}),
|
||||||
await this.prisma.trainTrip.count(),
|
await this.prisma.challenge.count(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,58 +41,4 @@ export class TrainsService {
|
|||||||
where: { id },
|
where: { id },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async import(user: User, { id: trainId }: ImportTrainDto): Promise<TrainTrip> {
|
|
||||||
const interrailResult: InterrailJourney = await fetch(`https://3uiwjsimnh.execute-api.eu-central-1.amazonaws.com/Prod/journey-import?id=${trainId}`)
|
|
||||||
.then(data => data.json())
|
|
||||||
if (interrailResult.data.travels.length !== 1)
|
|
||||||
throw new NotAcceptableException(`Ce voyage contient ${interrailResult.data.travels.length} trajets. Merci d'ajouter les trajets un à un.`)
|
|
||||||
const travel = interrailResult.data.travels[0]
|
|
||||||
if (travel.legs.length !== 1)
|
|
||||||
throw new NotAcceptableException(`Ce trajet contient ${travel.legs.length} trains. Merci d'ajouter les trajets un à un.`)
|
|
||||||
const leg = travel.legs[0]
|
|
||||||
|
|
||||||
const travelInfoJson: InterrailTravelInfo = JSON.parse(travel.infoJson)
|
|
||||||
const departure = new Date(`${travelInfoJson.date.year}-${travelInfoJson.date.month.toString().padStart(2, "0")}-${travelInfoJson.date.day.toString().padStart(2, "0")}` +
|
|
||||||
`T${travelInfoJson.departureTime.hours.toString().padStart(2, "0")}:${travelInfoJson.departureTime.minutes.toString().padStart(2, "0")}:00+0100`)
|
|
||||||
departure.setDate(departure.getDate() + travelInfoJson.departureTime.offset)
|
|
||||||
const arrival = new Date(`${travelInfoJson.date.year}-${travelInfoJson.date.month.toString().padStart(2, "0")}-${travelInfoJson.date.day.toString().padStart(2, "0")}` +
|
|
||||||
`T${travelInfoJson.arrivalTime.hours.toString().padStart(2, "0")}:${travelInfoJson.arrivalTime.minutes.toString().padStart(2, "0")}:00+0100`)
|
|
||||||
arrival.setDate(arrival.getDate() + travelInfoJson.arrivalTime.offset)
|
|
||||||
|
|
||||||
const legInfoJson: InterrailLegInfo = JSON.parse(leg.infoJson)
|
|
||||||
const distance = legInfoJson.trainStopStations
|
|
||||||
.map(trainStopStation => trainStopStation.coordinates)
|
|
||||||
.reduce((distance, coordinates, index) => {
|
|
||||||
if (index === 0)
|
|
||||||
return distance
|
|
||||||
const oldCoordinates = legInfoJson.trainStopStations.at(index - 1).coordinates
|
|
||||||
return distance + distanceCoordinates({ from: oldCoordinates, to: coordinates })
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
return this.prisma.trainTrip.upsert({
|
|
||||||
where: {
|
|
||||||
id: trainId,
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
id: trainId,
|
|
||||||
userId: user.id,
|
|
||||||
distance: distance,
|
|
||||||
from: travel.from,
|
|
||||||
to: travel.to,
|
|
||||||
departureTime: departure,
|
|
||||||
arrivalTime: arrival,
|
|
||||||
infoJson: leg.infoJson,
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
userId: user.id,
|
|
||||||
distance: distance,
|
|
||||||
from: travel.from,
|
|
||||||
to: travel.to,
|
|
||||||
departureTime: departure,
|
|
||||||
arrivalTime: arrival,
|
|
||||||
infoJson: leg.infoJson,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user