diff --git a/backend/src/place/dto/get-place.dto.ts b/backend/src/place/dto/get-place.dto.ts index c42c6983c403f9d7526a0047b72911150e67bafe..33dd5c6f95ca6e9705e792b198a9dadf4a795756 100644 --- a/backend/src/place/dto/get-place.dto.ts +++ b/backend/src/place/dto/get-place.dto.ts @@ -1,4 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; +import { Available } from 'src/pointOfInterest/enum/available.enum'; export class GetPlaceDto { @ApiProperty() @@ -10,7 +11,7 @@ export class GetPlaceDto { @ApiProperty() description: string; @ApiProperty() - available: string; + available: Available; @ApiProperty() imageName: string; @ApiProperty() diff --git a/backend/src/place/place.service.ts b/backend/src/place/place.service.ts index 312ff457379bd5cfc1b3dcc3ede402b488504c70..115604f7175fcb68604ecb2808e2ce9b1829285e 100644 --- a/backend/src/place/place.service.ts +++ b/backend/src/place/place.service.ts @@ -15,6 +15,7 @@ import { Available } from 'src/pointOfInterest/enum/available.enum'; import { UpdatePlaceReqDto } from './dto/update-place.req.dto'; import { Category } from 'src/category/entities/category.entity'; import { Visited } from 'src/visited/entities/visited.entity'; +import { isPlaceOpen } from './utils/isPlaceOpen'; @Injectable() export class PlaceService { @@ -230,4 +231,22 @@ export class PlaceService { // podemos hacerlo en la query return places.filter((place) => !visitedIds.includes(place.idPlace)); } + + async findPlacesNotVisitedByUserAndOpen(email: string, lang: LANGUAGES, idTown: number): Promise { + const places: GetPlaceDto[] = await this.findPlacesNotVisitedByUser(email, lang, idTown); + + const placesWithAvailability = await Promise.all( + places.map(async (place) => { + if (place.available === Available.CUSTOM) { + const availableDate = await this.availableDateRepository.findOneBy({ place }); + const isOpen = isPlaceOpen(place.available, availableDate?.startDate, availableDate?.endDate); + return isOpen ? place : null; + } else { + return isPlaceOpen(place.available, null, null) ? place : null; + } + }), + ); + + return placesWithAvailability.filter((place) => place !== null); + } } diff --git a/backend/src/place/utils/isPlaceOpen.ts b/backend/src/place/utils/isPlaceOpen.ts new file mode 100644 index 0000000000000000000000000000000000000000..0f64d5d854368bf23b6f8122317e627786eaae93 --- /dev/null +++ b/backend/src/place/utils/isPlaceOpen.ts @@ -0,0 +1,10 @@ +import { Available, availableToDays } from 'src/pointOfInterest/enum/available.enum'; + +export const isPlaceOpen = (available: Available, startDate?: Date, endDate?: Date): boolean => { + const curDayName: string = new Date().toLocaleDateString('en-US', { weekday: 'long' }); + if (available !== Available.CUSTOM) { + return availableToDays[available].includes(curDayName); + } + const curDate: Date = new Date(); + return curDate >= startDate && curDate <= endDate; +}; diff --git a/backend/src/pointOfInterest/PointOfInterest.service.ts b/backend/src/pointOfInterest/PointOfInterest.service.ts index 2ad45cb7a5c61ae182618e6069eba95e5de67a69..ed2b793a612d72470c9ee9598514028488c07a06 100644 --- a/backend/src/pointOfInterest/PointOfInterest.service.ts +++ b/backend/src/pointOfInterest/PointOfInterest.service.ts @@ -146,64 +146,60 @@ export class PointOfInterestService { return point; } - async findAllByIds(idPlace:number, pointsId: number[]): Promise { + async findAllByIds(idPlace: number, pointsId: number[]): Promise { const place = await this.placeService.findOne(idPlace); if (!place) { throw new BadRequestException('Place not found'); } - let points: printPointInfo[] = await Promise.all(pointsId.map(async (idPoint)=>{ - let point = await this.dataSource - .getRepository(PointOfInterest) - .createQueryBuilder('point') - .leftJoin('point.idPlace', 'place') - .select([ - 'point.idPoint as idPoint', - 'place.name as namePlace', - 'point.name as name', - ]) - .where('place.idPlace = :idPlace', { idPlace }) - .andWhere('point.idPoint = :idPoint', {idPoint}) - .getRawOne(); - return point; - })) + const points: printPointInfo[] = await Promise.all( + pointsId.map(async (idPoint) => { + const point = await this.dataSource + .getRepository(PointOfInterest) + .createQueryBuilder('point') + .leftJoin('point.idPlace', 'place') + .select(['point.idPoint as idPoint', 'place.name as namePlace', 'point.name as name']) + .where('place.idPlace = :idPlace', { idPlace }) + .andWhere('point.idPoint = :idPoint', { idPoint }) + .getRawOne(); + return point; + }), + ); return points; } - async generatePdf(idPlace:number, pointsId: number[]): Promise { + async generatePdf(idPlace: number, pointsId: number[]): Promise { const points = await this.findAllByIds(idPlace, pointsId); const htmlContent = this.generateHtml(points); const browser = await puppeteer.launch(); const page = await browser.newPage(); - + await page.setContent(htmlContent); const pdfBuffer = await page.pdf({ format: 'A4', margin: { top: '10mm', bottom: '10mm', left: '10mm', right: '10mm' }, // Márgenes }); - + await browser.close(); - + return Buffer.from(pdfBuffer); } private generateHtml(points: printPointInfo[]): string { let cardsHtml = ''; - for(const point of points) { + for (const point of points) { const filePath = join(ServerConstants.ROOT_STATIC_PATH, 'qr', point.idPoint.toString() + '.png'); - + cardsHtml += `
- +

${point.namePlace}

${point.name}

- +
`; } diff --git a/backend/src/pointOfInterest/entities/PointOfInterestTraduction.entity.ts b/backend/src/pointOfInterest/entities/PointOfInterestTraduction.entity.ts index f6cc23f380cab4d383bf3197571e0fea7f7b8db2..858f810319934456eb796c3cccf9fdb416e1bd4b 100644 --- a/backend/src/pointOfInterest/entities/PointOfInterestTraduction.entity.ts +++ b/backend/src/pointOfInterest/entities/PointOfInterestTraduction.entity.ts @@ -12,7 +12,7 @@ export class PointOfInterestTraduction { @Column({ nullable: false }) content: string; - @Column({ nullable: false }) + @Column({ nullable: true }) directions: string; @Column() diff --git a/backend/src/pointOfInterest/enum/available.enum.ts b/backend/src/pointOfInterest/enum/available.enum.ts index 5449782a25960c3b17b2f1ebfa49963bee68d087..3c70ab585ce88e04635e0c436a5a81e4df386aec 100644 --- a/backend/src/pointOfInterest/enum/available.enum.ts +++ b/backend/src/pointOfInterest/enum/available.enum.ts @@ -4,3 +4,9 @@ export enum Available { WEEKDAYS = 'weekdays', CUSTOM = 'custom', } + +export const availableToDays = { + [Available.WEEKEND]: ['Saturday', 'Sunday'], + [Available.ALL_DAYS]: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], + [Available.WEEKDAYS]: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'], +}; diff --git a/backend/src/route/route.service.ts b/backend/src/route/route.service.ts index 66a73c413084b201e6ea622b61ea7dae48397f59..572a18384134f7a0756e4135ff7366e5f5a9b1d4 100644 --- a/backend/src/route/route.service.ts +++ b/backend/src/route/route.service.ts @@ -31,7 +31,11 @@ export class RouteService { // Obtener los visitados y los candidatos const town: Town = await this.townRepository.findOneBy({ townId: idTown }); const user: User = await this.userRepository.findOneBy({ email }); - const placesNotVisited: GetPlaceDto[] = await this.placeService.findPlacesNotVisitedByUser(email, language, idTown); + const placesNotVisited: GetPlaceDto[] = await this.placeService.findPlacesNotVisitedByUserAndOpen( + email, + language, + idTown, + ); const visited: Visited[] = await this.visitedService.getVisitedByUser(email); const placesMapped: RecommendPlace[] = placesNotVisited.map((place) => { return { @@ -43,15 +47,13 @@ export class RouteService { }; }); - const visitedMapped: RecommendPlace[] = visited.map((visit) => { - return { - idPlace: visit.place.idPlace, - openAt: visit.place.openAt, - closeAt: visit.place.closeAt, - categories: visit.place.categories.map((category) => category.idCategory), - rating: visit.rating, - }; - }); + const visitedMapped: RecommendPlace[] = visited.map((visit) => ({ + idPlace: visit.place.idPlace, + openAt: visit.place.openAt, + closeAt: visit.place.closeAt, + categories: visit.place.categories.map((category) => category.idCategory), + rating: visit.rating, + })); const system = new RecommendationsSystem(visitedMapped, placesMapped, start, end); const chosen: RecommendPlace[] = system.recommend(); @@ -68,8 +70,10 @@ export class RouteService { const place = await this.placeService.findOneAndTradAndAvailable(curRecommended.idPlace, language); const endDate = new Date(); endDate.setHours(curRecommended.closeAt); + endDate.setMinutes(0); const startDate = new Date(); startDate.setHours(curRecommended.openAt); + startDate.setMinutes(0); const createTravelPlace: CreateTravelPlaceDto = { ...place, diff --git a/backend/src/route/utils/recommendations.ts b/backend/src/route/utils/recommendations.ts index 8fb0aa2e3688283730d38cc0c3e88ffcdc1a3ec8..971d113416030c3895a0becb84340d68f7fdd906 100644 --- a/backend/src/route/utils/recommendations.ts +++ b/backend/src/route/utils/recommendations.ts @@ -93,7 +93,7 @@ export class RecommendationsSystem { for (const place of validRecommendations) { // Verificar si el lugar está abierto y si se puede acomodar dentro del horario const maxStart = Math.max(currentTime, place.openAt); - const minEnd = Math.min((currentTime + 2) % 24, place.closeAt); + const minEnd = Math.min(this.end, place.closeAt); if (minEnd - 2 >= maxStart) { // Verificar si aún hay tiempo suficiente en el rango total de la ruta diff --git a/backend/src/user/user.service.ts b/backend/src/user/user.service.ts index 63b2c3ccc5b92c70798ee9a05b7a1d8d9e77f869..62e7b4d5521451543fdfe59bff71a0236d27d36b 100644 --- a/backend/src/user/user.service.ts +++ b/backend/src/user/user.service.ts @@ -17,8 +17,9 @@ export class UserService { private categoryService: CategoryService, ) {} - async findOne(email: string) { - return await this.userRepository.findOneBy({ email }); + async findOne(email: string): Promise { + const user = await this.userRepository.findOneBy({ email }); + return user; } async create(createUserDto: CreateUserDto) {