From 52a524c0384d973915d457ffbdc4fad4764c3ef7 Mon Sep 17 00:00:00 2001 From: Eshaal Syeda Date: Wed, 2 Jul 2025 16:17:56 +0000 Subject: [PATCH 1/8] Update README with Travel.AI project details --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index e69de29..e39e96c 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,29 @@ +# Travel.AI – AI-Powered Travel Recommendation App +Simple Python app that integrates the TripAdvisor API with Google Gemini API to recommend the +best hotels, restaurants, or attractions based on location. + +## General Info + +This project was developed as part of the SEO Tech Developer program. It demonstrates how to: +- Use APIs to fetch real-time data +- Store and manage results in a local database +- Integrate AI reasoning via Google Gemini + +## Technologies + +This project uses: + +- Python 3.11+ +- Pandas +- SQLAlchemy +- SQLite +- Requests + +## Setup Instructions + +---- + +## Contact + +William Du – [wiiidu315@gmail.com](mailto:wiiidu315@gmail.com) +Eshaal Syeda – [eshaal.syeda@richmond.edu](mailto:eshaal.syeda@richmond.edu) From b7fc72f344ac6c5991b3fd871c2928430e7a417f Mon Sep 17 00:00:00 2001 From: Eshaal Syeda Date: Wed, 2 Jul 2025 16:22:38 +0000 Subject: [PATCH 2/8] Save local changes before switching branches --- combined.py | 26 ++++++++++++++++++++------ json_parsing.py | 1 - 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/combined.py b/combined.py index eea161f..c807835 100644 --- a/combined.py +++ b/combined.py @@ -2,6 +2,7 @@ import requests from google import genai from google.genai import types +from json_parsing import Parser # Set environment variables TRIPADVISOR_API_KEY = os.getenv('TRIPADVISOR_API_KEY') @@ -24,23 +25,36 @@ } response = requests.get(url, params=data) +data_of_trip = response.json() +results = data_of_trip.get('data', []) -print(response.json()) +top_five = [] +for given in results[:5]: + name = given.get('name') + if name: + top_five.append(name) + +combined = ','.join(top_five) prompt = ( - f"recommend the top {cate} " - f"in {cy} and explain why it's better." + f"here are the top {cate}s " + f"in {cy}: (combined)." + "based on these, recommend the better one and why" + "respond in two to three sentences" ) # Specify the model to use and the messages to send -response = client.models.generate_content( +ai_response = client.models.generate_content( model="gemini-2.5-flash", config=types.GenerateContentConfig( system_instruction=( - "You are a nice travel assistant who gives helpful suggestions." + "You are a helpful travel assistant." ) ), contents=prompt, ) -print(response.text) +print(ai_response.text) + +parser = Parser(data_of_trip) +parser.write_to_database('locations') diff --git a/json_parsing.py b/json_parsing.py index 9503ff2..6e67eb8 100644 --- a/json_parsing.py +++ b/json_parsing.py @@ -3,7 +3,6 @@ import os import requests - class Parser: def __init__(self, json): From a86b0d5d8cdf5b01131b1a2c3f9e8b9d12eeb162 Mon Sep 17 00:00:00 2001 From: Eshaal Syeda Date: Wed, 2 Jul 2025 17:02:12 +0000 Subject: [PATCH 3/8] Updated parsing --- combined.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/combined.py b/combined.py index c807835..baf6c26 100644 --- a/combined.py +++ b/combined.py @@ -38,7 +38,7 @@ prompt = ( f"here are the top {cate}s " - f"in {cy}: (combined)." + f"in {cy}: {combined}." "based on these, recommend the better one and why" "respond in two to three sentences" ) @@ -55,6 +55,3 @@ ) print(ai_response.text) - -parser = Parser(data_of_trip) -parser.write_to_database('locations') From 143f3396e6d48b05eb9262080bdb6febbe0e6a32 Mon Sep 17 00:00:00 2001 From: Eshaal Syeda Date: Wed, 2 Jul 2025 17:15:28 +0000 Subject: [PATCH 4/8] updates --- combined.py | 1 + 1 file changed, 1 insertion(+) diff --git a/combined.py b/combined.py index baf6c26..81f681d 100644 --- a/combined.py +++ b/combined.py @@ -55,3 +55,4 @@ ) print(ai_response.text) + From f547f214402a13645b5ee6781674ac60eb62a986 Mon Sep 17 00:00:00 2001 From: Eshaal Syeda Date: Wed, 2 Jul 2025 18:47:08 +0000 Subject: [PATCH 5/8] checking --- combined.py | 1 - json_parsing.py | 52 +++++++++++++++++++++++-------------------------- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/combined.py b/combined.py index 81f681d..baf6c26 100644 --- a/combined.py +++ b/combined.py @@ -55,4 +55,3 @@ ) print(ai_response.text) - diff --git a/json_parsing.py b/json_parsing.py index 6e67eb8..a4cc188 100644 --- a/json_parsing.py +++ b/json_parsing.py @@ -1,11 +1,14 @@ +import json import sqlalchemy as db import pandas as pd import os import requests +from dotenv import load_dotenv -class Parser: +class Parser: def __init__(self, json): + load_dotenv() if 'data' in json: self.json = json['data'] else: @@ -16,7 +19,7 @@ def __init__(self, json): def write_to_database(self, tb_name): self.df.\ - to_sql(tb_name, con=self.engine, if_exists='append', index=True) + to_sql(tb_name, con=self.engine, if_exists='append', index=False) # removing duplicates, this should work with self.engine.connect() as connection: @@ -27,30 +30,23 @@ def write_to_database(self, tb_name): GROUP BY Name );""" connection.execute(db.text(remove_dupes)) - # query_result = - # connection.execute - # (db.text(f"SELECT * FROM {tb_name};")).fetchall() - # print(pd.DataFrame(query_result)) - self.get_ratings() - - def get_ratings(self): - if not isinstance(self.json, type([])): - return - for location in self.json: - location_id = location['location_id'] - url = """https://api.content.tripadvisor.com/ - api/v1/location/search?language=en""" - data = { - 'key': self.key, - 'locationId': location_id - } - - r = requests.get(url, data=data).json() - df = pd.json_normalize(r) - df.to_sql("temp", con=self.engine, if_exists='append', index=True) - - join_command = """CREATE TABLE recommendations AS - SELECT * FROM locations - JOIN temp ON locations.location_id = temp.location_id;""" + + def pull_list(self, table_name, city): + query = (f"SELECT * FROM {table_name} " + f"WHERE \"address_obj.city\" = '{city}' LIMIT 10;") with self.engine.connect() as connection: - connection.execute(db.text(join_command)) + result = connection.execute(db.text(query)).fetchall() + print(pd.DataFrame(result)) + + def drop(self, table_name): + command = f"DROP TABLE IF EXISTS {table_name}" + with self.engine.connect() as connection: + connection.execute(db.text(command)) + + +with open('sample2.txt', 'r') as file: + jackson = json.loads(file.read()) +# print(jackson) +test = Parser(jackson) +test.write_to_database("test") +test.pull_list("test", "Plano") From 43aa52736c0e84e128700ef48aaca480e6f04a3d Mon Sep 17 00:00:00 2001 From: Eshaal Syeda Date: Wed, 2 Jul 2025 19:33:27 +0000 Subject: [PATCH 6/8] changes to get opt from db --- combined.py | 53 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/combined.py b/combined.py index baf6c26..2d59f5d 100644 --- a/combined.py +++ b/combined.py @@ -26,32 +26,45 @@ response = requests.get(url, params=data) data_of_trip = response.json() -results = data_of_trip.get('data', []) +#results = data_of_trip.get('data', []) -top_five = [] +'''top_five = [] for given in results[:5]: name = given.get('name') if name: top_five.append(name) -combined = ','.join(top_five) +combined = ','.join(top_five)''' -prompt = ( - f"here are the top {cate}s " - f"in {cy}: {combined}." - "based on these, recommend the better one and why" - "respond in two to three sentences" -) -# Specify the model to use and the messages to send -ai_response = client.models.generate_content( - model="gemini-2.5-flash", - config=types.GenerateContentConfig( - system_instruction=( - "You are a helpful travel assistant." - ) - ), - contents=prompt, -) +parser = Parser(data_of_trip) +parser.write_to_database('locations') + +# Pull stored results +db_results = parser.pull_list("locations", cy) +for row in db_results: + name = row._mapping.get('name') + + prompt = ( + f"{name} is a {cate} " + f"in {cy}." + "respond in two to three sentences why its good" + ) + + # Specify the model to use and the messages to send + ai_response = client.models.generate_content( + model="gemini-2.5-flash", + config=types.GenerateContentConfig( + system_instruction=( + "You are a helpful travel assistant." + ) + ), + contents=prompt, + ) + + print(ai_response.text) -print(ai_response.text) + another_sugg = input("Do you want another suggestion? (Yes/No): ") + if another_sugg != "yes": + print("Enjoy!") + break From cfcb543048be8583fcb9f1d1b865f4b9f1f1fd1c Mon Sep 17 00:00:00 2001 From: Eshaal Syeda Date: Wed, 2 Jul 2025 19:44:54 +0000 Subject: [PATCH 7/8] updates --- combined.py | 2 +- json_parsing.py | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/combined.py b/combined.py index 2d59f5d..56e5250 100644 --- a/combined.py +++ b/combined.py @@ -64,7 +64,7 @@ print(ai_response.text) - another_sugg = input("Do you want another suggestion? (Yes/No): ") + another_sugg = input("Do you want another suggestion? (Yes/No): ").lower().strip() if another_sugg != "yes": print("Enjoy!") break diff --git a/json_parsing.py b/json_parsing.py index a4cc188..de8bc8e 100644 --- a/json_parsing.py +++ b/json_parsing.py @@ -36,17 +36,9 @@ def pull_list(self, table_name, city): f"WHERE \"address_obj.city\" = '{city}' LIMIT 10;") with self.engine.connect() as connection: result = connection.execute(db.text(query)).fetchall() - print(pd.DataFrame(result)) + return result def drop(self, table_name): command = f"DROP TABLE IF EXISTS {table_name}" with self.engine.connect() as connection: connection.execute(db.text(command)) - - -with open('sample2.txt', 'r') as file: - jackson = json.loads(file.read()) -# print(jackson) -test = Parser(jackson) -test.write_to_database("test") -test.pull_list("test", "Plano") From 2818e5532c63e1682a5f09efcd0fdd716b169f7b Mon Sep 17 00:00:00 2001 From: wiidu Date: Wed, 2 Jul 2025 15:09:37 -0500 Subject: [PATCH 8/8] Separate different categories into their own tables --- combined.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/combined.py b/combined.py index 56e5250..7841de4 100644 --- a/combined.py +++ b/combined.py @@ -3,8 +3,10 @@ from google import genai from google.genai import types from json_parsing import Parser +from dotenv import load_dotenv # Set environment variables +load_dotenv() TRIPADVISOR_API_KEY = os.getenv('TRIPADVISOR_API_KEY') GENAI_KEY = os.getenv('GENAI_KEY') @@ -38,10 +40,10 @@ parser = Parser(data_of_trip) -parser.write_to_database('locations') +parser.write_to_database(cate) # Pull stored results -db_results = parser.pull_list("locations", cy) +db_results = parser.pull_list(cate, cy) for row in db_results: name = row._mapping.get('name')