Added /timeslot endpoint
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
parent
c746675cf7
commit
24dff498f8
@ -0,0 +1,18 @@
|
||||
import Trainer from "./trainer"
|
||||
|
||||
export interface Timeslot {
|
||||
startTime: Date
|
||||
endTime: Date
|
||||
}
|
||||
|
||||
export interface WeeklyTimeslot {
|
||||
day_of_week: number
|
||||
start_time: string
|
||||
end_time: string
|
||||
}
|
||||
|
||||
export interface ReservedTimeslots {
|
||||
id: number
|
||||
startTime: string | Date
|
||||
endTime: string | Date
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { client } from "../db";
|
||||
|
||||
export async function trainerExists(req: Request, res: Response, next: NextFunction) {
|
||||
const trainerExistsResult = await client.query(`
|
||||
SELECT 1 FROM trainers where id = $1;
|
||||
`, [
|
||||
req.params.trainer_id
|
||||
]);
|
||||
|
||||
if (trainerExistsResult.rowCount < 1) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
return next();
|
||||
}
|
||||
@ -0,0 +1,167 @@
|
||||
import express, { Router, Request, Response, NextFunction } from "express";
|
||||
import Joi from "joi"
|
||||
import dayjs from "dayjs"
|
||||
import isoWeek from "dayjs/plugin/isoWeek"
|
||||
import utc from "dayjs/plugin/utc"
|
||||
|
||||
import { client } from "../db";
|
||||
import { DatabaseError } from "pg";
|
||||
import Trainer from "../interfaces/trainer";
|
||||
import { ReservedTimeslots, Timeslot, TrainerWeeklyTimeslot, WeeklyTimeslot } from "../interfaces/timeslot";
|
||||
import { TrainerAuth } from "../middlewares/auth";
|
||||
import { trainerExists } from "../middlewares/trainer";
|
||||
import { idSchema } from "../schemas";
|
||||
|
||||
dayjs.extend(isoWeek)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const router: Router = express.Router();
|
||||
|
||||
const timeSchema = Joi.string().regex(/^\d{1,2}:\d{1,2}$/);
|
||||
|
||||
const timeslotFiltersSchema = Joi.object({
|
||||
trainer: Joi.array().single().items(
|
||||
idSchema.required()
|
||||
),
|
||||
center: idSchema,
|
||||
startDate: Joi.date().default(Date.now()),
|
||||
endDate: Joi.date().default(Date.now()),
|
||||
startTime: timeSchema,
|
||||
endTime: timeSchema
|
||||
});
|
||||
|
||||
interface TimeslotFilters {
|
||||
trainer?: number[]
|
||||
center?: number
|
||||
startDate: Date
|
||||
endDate: Date
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
}
|
||||
|
||||
interface DatabaseResult {
|
||||
trainer: Trainer
|
||||
timeslots: WeeklyTimeslot[]
|
||||
reserved_timeslots: ReservedTimeslots[]
|
||||
}
|
||||
|
||||
interface TrainerWithAvailableTimeslots extends Trainer {
|
||||
timeslots: Timeslot[]
|
||||
}
|
||||
|
||||
router.get("/timeslot", async (req: Request, res: Response) => {
|
||||
try {
|
||||
|
||||
const validation = timeslotFiltersSchema.validate(req.query, { abortEarly: false, stripUnknown: true });
|
||||
if (validation.error !== undefined) {
|
||||
return res.status(400).send(validation.error.details);
|
||||
}
|
||||
|
||||
const timeslotFilters: TimeslotFilters = validation.value;
|
||||
|
||||
const filterStartDate: dayjs.Dayjs = dayjs(timeslotFilters.startDate).utcOffset(0).startOf("date");
|
||||
const filterEndDate: dayjs.Dayjs = dayjs(timeslotFilters.endDate).utcOffset(0).startOf("date");
|
||||
|
||||
let weekdays = [];
|
||||
for (let day: dayjs.Dayjs = filterStartDate; !day.isAfter(filterEndDate); day = day.add(1, "day")) {
|
||||
weekdays.push(day.isoWeekday());
|
||||
}
|
||||
|
||||
const queryResult = await client.query(`
|
||||
SELECT
|
||||
weekly_timeslots.trainer_id,
|
||||
json_build_object(
|
||||
'id',weekly_timeslots.trainer_id,
|
||||
'first_name',users.first_name,
|
||||
'last_name',users.last_name,
|
||||
'center_id',trainers.center_id,
|
||||
'center_name',centers.name
|
||||
) as trainer,
|
||||
json_agg(json_build_object(
|
||||
'day_of_week',day_of_week,
|
||||
'start_time',start_time,
|
||||
'end_time',end_time)
|
||||
) as timeslots,
|
||||
(
|
||||
SELECT json_agg(json_build_object(
|
||||
'id', id,
|
||||
'start_time', start_time,
|
||||
'end_time', end_time
|
||||
)) FROM public.reserved_timeslots
|
||||
WHERE
|
||||
(start_time between '2023-04-16' AND '2023-04-20'
|
||||
OR end_time between '2023-04-16' AND '2023-04-20')
|
||||
AND weekly_timeslots.trainer_id = reserved_timeslots.trainer_id
|
||||
) as reserved_timeslots
|
||||
FROM
|
||||
weekly_timeslots
|
||||
JOIN trainers ON weekly_timeslots.trainer_id = trainers.id
|
||||
JOIN users ON trainers.user_id = users.id
|
||||
JOIN centers ON trainers.center_id = centers.id
|
||||
WHERE
|
||||
((trainer_id = ANY($1)) OR $2)
|
||||
AND (day_of_week = ANY($3))
|
||||
GROUP BY
|
||||
weekly_timeslots.trainer_id,
|
||||
users.first_name,
|
||||
users.last_name,
|
||||
trainers.center_id,
|
||||
centers.name,
|
||||
trainer_id;
|
||||
`, [
|
||||
timeslotFilters.trainer !== undefined ? timeslotFilters.trainer : [1],
|
||||
timeslotFilters.trainer === undefined,
|
||||
weekdays
|
||||
]);
|
||||
|
||||
const databaseResult: DatabaseResult[] = queryResult.rows;
|
||||
|
||||
const trainers: TrainerWithAvailableTimeslots[] = [];
|
||||
|
||||
for (const trainer of databaseResult) {
|
||||
const trainerWithAvailableTimeslots: TrainerWithAvailableTimeslots = {
|
||||
...trainer.trainer,
|
||||
timeslots: []
|
||||
}
|
||||
|
||||
for (let day: dayjs.Dayjs = filterStartDate; !day.isAfter(filterEndDate); day = day.add(1, "day")) {
|
||||
const weekDay = day.isoWeekday();
|
||||
const reservedTimeslots = trainer.reserved_timeslots;
|
||||
|
||||
timeslots: for (const timeslot of trainer.timeslots) {
|
||||
if (timeslot.day_of_week !== weekDay)
|
||||
continue timeslots;
|
||||
const startTime = day.clone()
|
||||
.hour(parseInt(timeslot.start_time.split(":")[0]))
|
||||
.minute(parseInt(timeslot.start_time.split(":")[1]))
|
||||
.second(parseInt(timeslot.start_time.split(":")[2]));
|
||||
const endTime = day.clone()
|
||||
.hour(parseInt(timeslot.end_time.split(":")[0]))
|
||||
.minute(parseInt(timeslot.end_time.split(":")[1]))
|
||||
.second(parseInt(timeslot.end_time.split(":")[2]));
|
||||
for (const reservedTimeslot of reservedTimeslots) {
|
||||
const reservedTimeStart = dayjs(reservedTimeslot.startTime);
|
||||
const reservedTimeEnd = dayjs(reservedTimeslot.endTime);
|
||||
if ((!reservedTimeStart.isBefore(startTime) && !reservedTimeStart.isAfter(endTime)) ||
|
||||
(!reservedTimeEnd.isBefore(startTime) && !reservedTimeStart.isAfter(endTime))) {
|
||||
continue timeslots;
|
||||
}
|
||||
}
|
||||
trainerWithAvailableTimeslots.timeslots.push({
|
||||
startTime: startTime.toDate(),
|
||||
endTime: endTime.toDate()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
trainers.push(trainerWithAvailableTimeslots);
|
||||
}
|
||||
|
||||
return res.status(200).send(trainers);
|
||||
} catch (error: DatabaseError | Error | any) {
|
||||
console.error(error);
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
})
|
||||
|
||||
export default router;
|
||||
@ -0,0 +1,3 @@
|
||||
import Joi from "joi"
|
||||
|
||||
export const idSchema = Joi.number().integer().positive();
|
||||
Loading…
Reference in New Issue