Loading backend/src/route/utils/recommendations.ts +64 −9 Original line number Diff line number Diff line import { RecommendPlace } from '../dto/recommend-route.dto'; import { DataFrame, Series } from './math'; import { customSort } from './sort'; import { customSort, sortByClose } from './sort'; export class RecommendationsSystem { getCategories(places: RecommendPlace[]): number[] { private visited: RecommendPlace[] = []; private candidates: RecommendPlace[] = []; private start: number = 0; private end: number = 0; constructor(visited: RecommendPlace[], candidates: RecommendPlace[], start: number, end: number) { this.visited = visited; this.candidates = candidates; this.start = start; this.end = end; } private getCategories(places: RecommendPlace[]): number[] { const categories = new Set<number>(); for (const place of places) { for (const category of place.categories) { Loading @@ -13,7 +25,7 @@ export class RecommendationsSystem { return Array.from(categories); } oneHotEncode(categories: number[], placesToEncode: RecommendPlace[]): DataFrame { private oneHotEncode(categories: number[], placesToEncode: RecommendPlace[]): DataFrame { const data: DataFrame = {}; for (const category of categories) { data[category] = []; Loading @@ -24,7 +36,7 @@ export class RecommendationsSystem { return data; } rankVisited(visited: RecommendPlace[]): Series { private rankVisited(visited: RecommendPlace[]): Series { const visitedCategories = this.getCategories(visited); const visitedEncoded = this.oneHotEncode(visitedCategories, visited); const grades = visited.map((place) => place.rating); Loading @@ -44,7 +56,7 @@ export class RecommendationsSystem { return result; } rankCandidates(candidates: RecommendPlace[], visited: Series): [number, number][] { private rankCandidates(candidates: RecommendPlace[], visited: Series): [number, number][] { const visitedCategories = Object.keys(visited).map(Number); const candidatesEncoded = this.oneHotEncode(visitedCategories, candidates); Loading @@ -62,9 +74,52 @@ export class RecommendationsSystem { return ranked; } recommend(visited: RecommendPlace[], candidates: RecommendPlace[]) { const visitedRanking = this.rankVisited(visited); const candidatesRanked = this.rankCandidates(candidates, visitedRanking); return candidatesRanked; takeTopNValid(recommendations: [number, number][]): RecommendPlace[] { const validRecommendations: RecommendPlace[] = []; // Filtrar los lugares que existen en candidates for (const [id] of recommendations) { const place = this.candidates.find((candidate) => candidate.idPlace === id); if (place) { validRecommendations.push(place); } } validRecommendations.sort(sortByClose); const chosen: RecommendPlace[] = []; let currentTime = this.start; // Tiempo inicial de la ruta 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); if (minEnd - 2 >= maxStart) { // Verificar si aún hay tiempo suficiente en el rango total de la ruta if ((currentTime + 2) % 24 <= this.end) { // Actualizar el lugar con el nuevo horario dentro de la ruta place.openAt = Math.max(currentTime, place.openAt); place.closeAt = (place.openAt + 2) % 24; chosen.push(place); // Actualizar la hora actual para el siguiente lugar currentTime = (currentTime + 2) % 24; } else { // Si ya no hay tiempo suficiente para otro lugar, terminamos el bucle break; } } } return chosen; } recommend(): RecommendPlace[] { const visitedRanking = this.rankVisited(this.visited); const candidatesRanked = this.rankCandidates(this.candidates, visitedRanking); const chosen = this.takeTopNValid(candidatesRanked); return chosen; } } Loading
backend/src/route/utils/recommendations.ts +64 −9 Original line number Diff line number Diff line import { RecommendPlace } from '../dto/recommend-route.dto'; import { DataFrame, Series } from './math'; import { customSort } from './sort'; import { customSort, sortByClose } from './sort'; export class RecommendationsSystem { getCategories(places: RecommendPlace[]): number[] { private visited: RecommendPlace[] = []; private candidates: RecommendPlace[] = []; private start: number = 0; private end: number = 0; constructor(visited: RecommendPlace[], candidates: RecommendPlace[], start: number, end: number) { this.visited = visited; this.candidates = candidates; this.start = start; this.end = end; } private getCategories(places: RecommendPlace[]): number[] { const categories = new Set<number>(); for (const place of places) { for (const category of place.categories) { Loading @@ -13,7 +25,7 @@ export class RecommendationsSystem { return Array.from(categories); } oneHotEncode(categories: number[], placesToEncode: RecommendPlace[]): DataFrame { private oneHotEncode(categories: number[], placesToEncode: RecommendPlace[]): DataFrame { const data: DataFrame = {}; for (const category of categories) { data[category] = []; Loading @@ -24,7 +36,7 @@ export class RecommendationsSystem { return data; } rankVisited(visited: RecommendPlace[]): Series { private rankVisited(visited: RecommendPlace[]): Series { const visitedCategories = this.getCategories(visited); const visitedEncoded = this.oneHotEncode(visitedCategories, visited); const grades = visited.map((place) => place.rating); Loading @@ -44,7 +56,7 @@ export class RecommendationsSystem { return result; } rankCandidates(candidates: RecommendPlace[], visited: Series): [number, number][] { private rankCandidates(candidates: RecommendPlace[], visited: Series): [number, number][] { const visitedCategories = Object.keys(visited).map(Number); const candidatesEncoded = this.oneHotEncode(visitedCategories, candidates); Loading @@ -62,9 +74,52 @@ export class RecommendationsSystem { return ranked; } recommend(visited: RecommendPlace[], candidates: RecommendPlace[]) { const visitedRanking = this.rankVisited(visited); const candidatesRanked = this.rankCandidates(candidates, visitedRanking); return candidatesRanked; takeTopNValid(recommendations: [number, number][]): RecommendPlace[] { const validRecommendations: RecommendPlace[] = []; // Filtrar los lugares que existen en candidates for (const [id] of recommendations) { const place = this.candidates.find((candidate) => candidate.idPlace === id); if (place) { validRecommendations.push(place); } } validRecommendations.sort(sortByClose); const chosen: RecommendPlace[] = []; let currentTime = this.start; // Tiempo inicial de la ruta 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); if (minEnd - 2 >= maxStart) { // Verificar si aún hay tiempo suficiente en el rango total de la ruta if ((currentTime + 2) % 24 <= this.end) { // Actualizar el lugar con el nuevo horario dentro de la ruta place.openAt = Math.max(currentTime, place.openAt); place.closeAt = (place.openAt + 2) % 24; chosen.push(place); // Actualizar la hora actual para el siguiente lugar currentTime = (currentTime + 2) % 24; } else { // Si ya no hay tiempo suficiente para otro lugar, terminamos el bucle break; } } } return chosen; } recommend(): RecommendPlace[] { const visitedRanking = this.rankVisited(this.visited); const candidatesRanked = this.rankCandidates(this.candidates, visitedRanking); const chosen = this.takeTopNValid(candidatesRanked); return chosen; } }