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