diff --git a/.gitignore b/.gitignore index e55ba43b360ae2b7d7c8f6d89170d2ea189169b7..dc2ed0bac8b328324063aaabc02935876499d77e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ microservice-users/__pycache__/main.cpython-311.pyc api-gateway/__pycache__/main.cpython-313.pyc microservice-products/__pycache__/main.cpython-313.pyc frontend/node_modules -frontend/.vite \ No newline at end of file +frontend/.vite +node_modules \ No newline at end of file diff --git a/frontend/src/components/components/Recommendations/RecommendationService.ts b/frontend/src/components/components/Recommendations/RecommendationService.ts new file mode 100644 index 0000000000000000000000000000000000000000..90c306c3840494d91a2dff48efa9e8e888b8261a --- /dev/null +++ b/frontend/src/components/components/Recommendations/RecommendationService.ts @@ -0,0 +1,12 @@ +// apiService.js + +export const handleRecommendedEvents = async (userId: any) => { + try { + const res = await fetch(`http://localhost:8005/suggestions?userId=${userId}`); + const data = await res.json(); // or res.json() if it's JSON + return data; + } catch (err:any) { + return `Error: ${err.message}`; + } + }; + \ No newline at end of file diff --git a/frontend/src/components/components/Recommendations/Recommendations.jsx b/frontend/src/components/components/Recommendations/Recommendations.jsx index 76b52afb752b2c260c9461874eae496af00f5b51..4f6c70d8fec3fe71ac0730a1f58262c54b30b338 100644 --- a/frontend/src/components/components/Recommendations/Recommendations.jsx +++ b/frontend/src/components/components/Recommendations/Recommendations.jsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react' +import { handleRecommendedEvents } from './RecommendationService' import './Recommendations.css' function RecommendationList(){ @@ -16,18 +17,20 @@ function RecommendationList(){ const [error, setError] = useState(null); useEffect(() => { - const fetchEvents = async () => { + const fetchEvents = async () => { try { - setRecommendation(fakeEvents) + const userId = '55555' + const data = await handleRecommendedEvents(userId); + setRecommendation(data); } catch (err) { - setError(err.message); // Catch errors and set error state + setError(err.message); } finally { - setLoading(false); // Stop loading + setLoading(false); } - }; - - fetchEvents(); - }, []); + }; + + fetchEvents(); + }, []); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; @@ -36,13 +39,12 @@ function RecommendationList(){ <> <h1>Recommendations</h1> <ul className='rcmd-list'> - {recommendations.map((recommendation)=>( - <li key={recommendation.id} className='rcmd-event'> - <h3>{recommendation.name}</h3> - <p>P{recommendation.date}</p> - <p>{recommendation.location}</p> - </li> - ))} + {recommendations.map((recommendation, index) => ( + <li key={index} className='rcmd-event'> + <h3>{recommendation.name}</h3> + {recommendation.score && <p>Score: {recommendation.score}</p>} + </li> + ))} </ul> </> ) diff --git a/microservice-recommendation/routes/suggestions.js b/microservice-recommendation/routes/suggestions.js index 1b48a0660830d6cf42966cc356c02f79c75f4bb5..71bcf5c95ae086b392d00b4803a52753215c39f0 100644 --- a/microservice-recommendation/routes/suggestions.js +++ b/microservice-recommendation/routes/suggestions.js @@ -13,53 +13,62 @@ const User = require('../models/users'); router.get('/', async (req, res)=>{ try { - const {userId} = req.query - if(!userId) return res.status(400).json({message:'Missing user ID'}) - const user = await User.findOne({userId}) - if(!user) return res.status(400).json({message: 'User not found!'}) - - //find events that have matching tags to the users liked tags - const likedTags = user.likedTags; - // $in is a mongoDB operator that matches values in an array field - // tags is the name of the field to query, likedTags are what we are interested in - // this will return any event that has at least one match with the users liked tags - let events = await Event.find({tags: {$in:likedTags}}) - - // assess how close an events tag match users liked tags - // assess the events average rating - // Event score = (tag match* weight) + (average rating*weight) + // Extract userId from query + const { userId } = req.query; + console.log('Hardcoded userId from frontend:', userId); + + // === PLACEHOLDER: Validate userId and find user === + // if (!userId) return res.status(400).json({ message: 'Missing user ID' }); + + // const user = await User.findOne({ userId }); + // if (!user) return res.status(400).json({ message: 'User not found!' }); + + // === DUMMY: Simulated liked tags for the user === + const likedTags = ['tech', 'music', 'outdoors']; + + // === PLACEHOLDER: Get all events with at least one matching tag === + // let events = await Event.find({ tags: { $in: likedTags } }); + + // === DUMMY: Simulated event data === + const events = [ + { id: 1, name: 'basketball', tags: ['tech', 'innovation'] }, + { id: 2, name: 'michael jackson', tags: ['outdoors', 'music'] }, + { id: 3, name: 'pdd', tags: ['literature'] }, + { id: 4, name: 'Fake event!!!!!!', tags: ['business', 'tech'] }, + ]; + const tagMatchWeight = 0.7; const reviewScoreWeight = 0.3; - - //wait for all events to finish before moving on - // for every event in events(all events with liked tags): - // find how similar users liked tags is to the events tags - const eventScores = await Promise.all(events.map(async(event)=>{ - - const matchingTags = event.tags.filter(value=>likedTags.includes(value)) //.filter returns what match condition inside param - const tagMatchScore = matchingTags.length / event.tags.length; // Percentage of tags that match - - //TODO: NEED TO SEE HOW THIS COMES OUT FROM REVIEW MICROSERVICE - // THIS IS WITH ASSUMPTION THAT EVENT OVERALL RATING IS ALREADY CALCULATED - const eventRating = 0 - const reviewScore = eventRating / 5; - - const finalScore = (tagMatchScore * tagMatchWeight) + (reviewScore * reviewScoreWeight) - - return {event, finalScore} // end up with array of {event: {name, tags[]}, fianlsore: } - })) - - //sort them by finalScore, .sort((a,b)) take two elements and compare them, - eventOrdered = eventScores.sort((a,b)=> b.finalScore - a.finalScore) - - //TODO: - // HOW SHOULD I RESPOND THIS? NAME SHOULD PROABBLY BE ENOUGH, BUT SHOULD DISCUSS THIS WITH EVENTS MISCROSERVICE - - - - } catch (error) { - res.status(500).json({ message: 'Error fetching recommendations', error }) - } -}) + + // Score each event + const eventScores = await Promise.all(events.map(async (event) => { + const matchingTags = event.tags.filter(tag => likedTags.includes(tag)); + const tagMatchScore = matchingTags.length / event.tags.length; + + // TODO: Replace with actual rating from review microservice + const eventRating = 4; // hardcoded out of 5 + const reviewScore = eventRating / 5; + + const finalScore = (tagMatchScore * tagMatchWeight) + (reviewScore * reviewScoreWeight); + + return { event, finalScore }; + })); + + // Sort events by score (highest to lowest) + const eventOrdered = eventScores.sort((a, b) => b.finalScore - a.finalScore); + + // TODO: Customize response shape later + const response = eventOrdered.map(({ event, finalScore }) => ({ + name: event.name, + score: finalScore.toFixed(2), + })); + + res.json(response); + + } catch (err) { + console.error("Error fetching recommendations:", err); + res.status(500).json({ message: 'Server error fetching recommendations.' }); + } + }); module.exports = router \ No newline at end of file diff --git a/microservice-recommendation/routes/tags.js b/microservice-recommendation/routes/tags.js index cd15722150d41f9b9978106e91e7fc3dd105703d..54dbd634371199ec6491decebbfa34eb4ca19053 100644 --- a/microservice-recommendation/routes/tags.js +++ b/microservice-recommendation/routes/tags.js @@ -5,7 +5,7 @@ const Tags = require('../models/tags') const User = require('../models/users') // get all tag -router.get('/tags', async (req, res)=>{ +router.get('/', async (req, res)=>{ try { const tagsdb = await Tags.find() res.json(tagsdb) @@ -16,13 +16,6 @@ router.get('/tags', async (req, res)=>{ // save tags, and create user for recommendation microservice router.post('/:id/save-tags', async(req, res)=>{ - // endpoint `GET /likedTags/{id}` from users - // { - // "likedTags": [ - // "tag1", - // "tag2" - // ] - // } try { // depends how we handle api calls, directly or route through frontend? diff --git a/microservice-recommendation/server.js b/microservice-recommendation/server.js index 57ab91488baa2c27eebb71952c21e3a056177666..d8db4a07532dbe5bd51c4119c92f389812a15af2 100644 --- a/microservice-recommendation/server.js +++ b/microservice-recommendation/server.js @@ -3,13 +3,15 @@ require('dotenv').config() const express = require('express') const app = express() const mongoose = require('mongoose') +const cors = require('cors') -mongoose.connect(process.env.DOCKER_DATABASE_URL) +mongoose.connect(process.env.LOCAL_DATABASE_URL) const db = mongoose.connection db.on('error', (error) => console.error(error)) db.once('open', () => console.log('connected to database')) app.use(express.json()) +app.use(cors()) app.get('/', (req, res) => { res.status(200).json({