import spacy.cli spacy.cli.download("en_core_web_sm") import os import gradio as gr from groq import Groq from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import letter import tempfile import re from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer from reportlab.lib.styles import getSampleStyleSheet from reportlab.lib.pagesizes import letter import spacy from geopy.geocoders import Nominatim import folium # Load API key GR_API_KEY = os.getenv("GR_API_KEY") client = Groq(api_key=GR_API_KEY) MOODS = [ "Adventure", "Relaxing", "Romantic", "Cultural & Heritage", "Foodie & Culinary", "Shopping & Urban", "Beach & Island", "Any" ] TRAVELLER_TYPES = [ "Solo", "Couple", "Family", "Friends", "Business", "Any" ] MOOD_IMAGES = { "Adventure": "images/adventure.png", "Relaxing": "images/relaxing.png", "Romantic": "images/romantic.png", "Cultural & Heritage": "images/cultural.png", "Foodie & Culinary": "images/foodie.png", "Shopping & Urban": "images/shopping.png", "Beach & Island": "images/beach.png", "Any": "images/default.png" } nlp = spacy.load("en_core_web_sm") def clean_place_name(name): return re.sub(r'^(the|a|an)\s+', '', name, flags=re.IGNORECASE).strip() def extract_places_and_map(itinerary_text, city_context=None): pattern = r"(visit|explore|trip to|at|go to|see)\s+([A-Z][A-Za-zÀ-ÖØ-öø-ÿ'’\- ]+)" matches = re.findall(pattern, itinerary_text, re.IGNORECASE) rule_places = [m[1].strip() for m in matches] doc = nlp(itinerary_text) ner_places = [ent.text for ent in doc.ents if ent.label_ in ["GPE", "LOC", "FAC"]] places = list(set(rule_places + ner_places)) geolocator = Nominatim(user_agent="feelaway_app") locations = [] for place in places: try: query = f"{place}, {city_context}" if city_context else place loc = geolocator.geocode(query, language='en', timeout=10) if loc: locations.append((place, loc.latitude, loc.longitude)) except Exception: continue if not locations: return "
No locations found for mapping.
" m = folium.Map(location=[locations[0][1], locations[0][2]], zoom_start=10) for name, lat, lon in locations: folium.Marker([lat, lon], popup=name).add_to(m) return m._repr_html_() def generate_itinerary(mood, location, budget, days, traveller_type): mood_text = "any mood" if mood == "Any" else mood budget_text = "any budget" if budget == "Any" else budget traveller_text = "any traveller" if traveller_type == "Any" else traveller_type location_text = location if location.strip() != "" else "any destination" prompt = ( f"Create a {days}-day travel itinerary for a {traveller_text} visiting {location_text}. " f"Include the best season to visit the location. " f"The traveler's mood is {mood_text} and the budget is {budget_text}. " f"Include daily activities, key landmarks, meals, and cultural experiences. " f"Use Markdown formatting with **bold headings** for days and time slots." ) completion = client.chat.completions.create( model="llama-3.1-8b-instant", messages=[ {"role": "system", "content": "You are an expert travel planner."}, {"role": "user", "content": prompt} ], temperature=1, max_completion_tokens=1024, top_p=1, stream=False ) itinerary_text = completion.choices[0].message.content image_path = os.path.abspath(MOOD_IMAGES.get(mood, MOOD_IMAGES["Any"])) map_html = extract_places_and_map(itinerary_text, city_context=location_text if location_text.lower() != "any destination" else None) return itinerary_text, image_path, map_html def generate_pdf(itinerary_text): if not itinerary_text.strip(): return None, gr.update(visible=False) temp_dir = tempfile.gettempdir() temp_path = os.path.join(temp_dir, "feelaway_itinerary.pdf") if os.path.exists(temp_path): os.remove(temp_path) doc = SimpleDocTemplate(temp_path, pagesize=letter, rightMargin=40, leftMargin=40, topMargin=40, bottomMargin=40) styles = getSampleStyleSheet() style = styles["Normal"] style.fontName = "Times-Roman" style.fontSize = 14 style.leading = 18 html_text = re.sub(r'\*\*(.+?)\*\*', r'\1', itinerary_text) paragraphs = html_text.split('\n') story = [] for para in paragraphs: p = Paragraph(para if para.strip() else ' ', style) story.append(p) story.append(Spacer(1, 6)) doc.build(story) return temp_path, gr.update(visible=True) # ---------------------- UI ---------------------- with gr.Blocks(theme=gr.themes.Default(primary_hue="teal", secondary_hue="teal")) as demo: header_html = """