import pandas as pd
import json
import os
import random
from chat_a import (
analyze_emotion,
detect_intent,
extract_themes,
recommend_places_by_theme,
detect_location_filter,
generate_intro_message,
theme_ui_map,
ui_to_theme_map,
theme_opening_lines,
intent_opening_lines,
apply_weighted_score_filter,
get_highlight_message,
get_weather_message,
get_intent_intro_message,
recommend_packages,
handle_selected_place,
generate_region_intro,
parse_companion_and_age,
filter_packages_by_companion_age,
make_top2_description_custom,
format_summary_tags_custom,
make_companion_age_message
)
import streamlit as st
from streamlit.components.v1 import html
from css import render_message, render_chip_buttons, log_and_render, replay_log
import streamlit as st, pandas as pd, requests, json
st.success("π μ±μ΄ μ±κ³΅μ μΌλ‘ μμλμμ΅λλ€! λΌμ΄λΈλ¬λ¦¬ μ€μΉ μ±κ³΅!")
@st.cache_data(show_spinner=False)
def load_csv_any(p):
return pd.read_csv(p) if str(p).startswith(("http://","https://")) else pd.read_csv(p)
# λ°μ΄ν° λ‘λ©μ μν ν¨μ
@st.cache_data
def load_travel_data(file_path):
print(f"Caching {file_path}...") # μΊμκ° μΈμ μ€νλλμ§ νμΈμ©
return pd.read_csv(file_path)
@st.cache_data
def load_json_data(file_path):
print(f"Caching {file_path}...")
with open(file_path, "r", encoding="utf-8") as f:
return json.load(f)
# βββββββββββββββββββββββββββββββββββββ λ°μ΄ν° λ‘λ
# trip_url = st.secrets.get("TRIPDATA_URL")
# if not trip_url:
# st.error("TRIPDATA_URL λ―Έμ€μ : Streamlit Secretsμ URLμ λ£μ΄μ£ΌμΈμ.")
# st.stop()
travel_df = load_travel_data("νΈλ¦½λ·μ»΄_κ°μ _ν
λ§_νμ€μ€λͺ
_ν΅ν©_07_08.csv")
external_score_df = load_travel_data("ν΄λ¬μ€ν°_ν¬ν¨_μΈλΆμμΈ_μ’
ν©μ μ_κ²°κ³Ό_μ΅μ’
.csv")
festival_df = load_travel_data("μ μ²λ¦¬_ν΅ν©μ§μμΆμ .csv")
weather_df = load_travel_data("μ μ²λ¦¬_λ μ¨_ν΅ν©_07_08.csv")
package_df = load_travel_data("λͺ¨λν¬μ΄_컬λΌλ³_κ°μ_07_08.csv")
master_df = load_travel_data("λλΌ_λμ_리μ€νΈ.csv")
theme_title_phrases = load_json_data("theme_title_phrases.json")
# βββββββββββββββββββββββββββββββββββββ streamlitμ© ν¨μ
def init_session():
if "chat_log" not in st.session_state:
st.session_state.chat_log = []
if "mode" not in st.session_state:
st.session_state.mode = None
if "user_input" not in st.session_state:
st.session_state.user_input = ""
if "selected_theme" not in st.session_state:
st.session_state.selected_theme = None
def make_key(row) -> tuple[str, str]:
"""prev μ λ£κ³ κΊΌλΌ λ μ°λ κ³ μ ν€(μ¬νμ§, μ¬νλμ)"""
return (row["μ¬νμ§"], row["μ¬νλμ"])
# ββ P κΈκΌ΄ ν¬κΈ° 14 px βββββββββββββββββββββββββββββββββββ
st.markdown("""
""", unsafe_allow_html=True)
# βββββββββββββββββββββββββββββββββββββ region mode
def region_ui(travel_df, external_score_df, festival_df, weather_df, package_df,
country_filter, city_filter, chat_container, log_and_render):
"""region λͺ¨λ(νΉμ λλΌ, λμλ₯Ό μ§μ μΈκΈνμ κ²½μ°) μ μ© UI & λ‘μ§"""
# ββββββββββββββββββ μΈμ
ν€ μ μ
region_key = "region_chip_selected"
prev_key = "region_prev_recommended"
step_key = "region_step"
sample_key = "region_sample_df"
# ββββββββββββββββββ 0) μ΄κΈ°ν
if step_key not in st.session_state:
st.session_state[step_key] = "recommend"
st.session_state[prev_key] = set()
st.session_state.pop(sample_key, None)
# ββββββββββββββββββ 1) restart μνλ©΄ μΈνΈλ‘λ§ μΆλ ₯νκ³ μ’
λ£
if st.session_state[step_key] == "restart":
log_and_render(
"λ€μ μ¬νμ§λ₯Ό μΆμ²ν΄λ릴κ²μ!
μμ¦ λ μ€λ₯΄λ μ¬νμ΄ μμΌμ κ°μ?",
sender="bot",
chat_container=chat_container,
key="region_restart_intro"
)
return
# ββββββββββββββββββ 2) μΆμ² λ¨κ³
if st.session_state[step_key] == "recommend":
# 2.1) μΆμ² 문ꡬ μΆλ ₯ (λμ λλ κ΅κ° κΈ°μ€)
city_exists = bool(city_filter) and city_filter in travel_df["μ¬νλμ"].values
country_exists = bool(country_filter) and country_filter in travel_df["μ¬νλλΌ"].values
# μ‘΄μ¬νμ§ μλ λμμΈ κ²½μ°
if city_filter and not city_exists:
intro = generate_region_intro('', country_filter)
log_and_render(
f"μ£μ‘ν΄μ. {city_filter}μ μ¬νμ§λ μμ§ λ―Έμ μ΄μμ.
νμ§λ§, {intro}",
sender="bot",
chat_container=chat_container,
key="region_intro_invalid"
)
else:
# μ μμ μΈ λμ/κ΅κ°μΌ κ²½μ°
intro = generate_region_intro(city_filter, country_filter)
log_and_render(intro,
sender="bot",
chat_container=chat_container,
key="region_intro")
# 2.2) μ¬νμ§ ν보 λͺ©λ‘ νν°λ§
df = travel_df.drop_duplicates(subset=["μ¬νμ§"])
if city_exists:
df = df[df["μ¬νλμ"].str.contains(city_filter, na=False)]
elif country_exists:
df = df[df["μ¬νλλΌ"].str.contains(country_filter, na=False)]
# 2.3) μ΄μ μΆμ² λͺ©λ‘κ³Ό κ²ΉμΉμ§ μλ μ¬νμ§λ§ λ¨κΉ
prev = st.session_state.setdefault(prev_key, set())
remaining = df[~df.apply(lambda r: make_key(r) in prev, axis=1)]
# μΆμ² κ°λ₯ν μ¬νμ§κ° μλ€λ©΄ μ’
λ£ λ¨κ³λ‘ μ ν
if remaining.empty and sample_key not in st.session_state:
st.session_state[step_key] = "recommand_end"
st.rerun()
return
# 2.4) μνλ§ (μ΄μ μνμ΄ μκ±°λ λΉμ΄ μμΌλ©΄ μλ‘ μΆμΆ)
if sample_key not in st.session_state or st.session_state[sample_key].empty:
sampled = remaining.sample(
n=min(3, len(remaining)), #μ΅λ 3κ°
random_state=random.randint(1, 9999)
)
st.session_state[sample_key] = sampled
# tuple ννλ‘ νκΊΌλ²μ μΆκ°
prev.update([make_key(r) for _, r in sampled.iterrows()])
st.session_state[prev_key] = prev
else:
sampled = st.session_state[sample_key]
loc_df = st.session_state[sample_key]
# 2.5) μΆμ² 리μ€νΈ μΆλ ₯ & μΉ© UI
message = (
"π μΆμ² μ¬νμ§ λͺ©λ‘
κ°μ₯ κ°κ³ μΆμ κ³³μ 골λΌμ£ΌμΈμ!
" +
"
".join([
f"{i+1}. {row.μ¬νμ§} "
f"({row.μ¬νλλΌ}, {row.μ¬νλμ}) "
f"{getattr(row, 'νμ€μ€λͺ
', 'μ€λͺ
μ΄ μμ΅λλ€')}"
for i, row in enumerate(loc_df.itertuples())
])
)
with chat_container:
log_and_render(message,
sender="bot",
chat_container=chat_container,
key=f"region_recommendation_{random.randint(1,999999)}"
)
# μΉ© λ²νΌμΌλ‘ μΆμ²μ§ μ€ μ νλ°κΈ°
prev_choice = st.session_state.get(region_key, None)
choice = render_chip_buttons(
loc_df["μ¬νμ§"].tolist() + ["λ€λ₯Έ μ¬νμ§ λ³΄κΈ° π"],
key_prefix="region_chip",
selected_value=prev_choice
)
# 2.7) μ ν κ²°κ³Ό μ²λ¦¬
if not choice or choice == prev_choice:
return
if choice == "λ€λ₯Έ μ¬νμ§ λ³΄κΈ° π":
log_and_render("λ€λ₯Έ μ¬νμ§ λ³΄κΈ° π",
sender="user",
chat_container=chat_container,
key=f"user_place_refresh_{random.randint(1,999999)}")
st.session_state.pop(sample_key, None)
st.rerun()
return
# 2.8) μ¬νμ§ μ ν μλ£
st.session_state[region_key] = choice
st.session_state[step_key] = "detail"
st.session_state.chat_log.append(("user", choice))
# μ€μ λ‘ μ νλ μ¬νμ§λ§ prevμ κΈ°λ‘
match = sampled[sampled["μ¬νμ§"] == choice]
if not match.empty:
prev.add(make_key(match.iloc[0]))
st.session_state[prev_key] = prev
# μν νκΈ°
st.session_state.pop(sample_key, None)
st.rerun()
return
# ββββββββββββββββββ 3) μΆμ² μ’
λ£ λ¨κ³: λ μ΄μ μΆμ²ν μ¬νμ§κ° μμ λ
elif st.session_state[step_key] == "recommand_end":
with chat_container:
# 3.1) λ©μμ§ μΆλ ₯
log_and_render(
"β οΈ λ μ΄μ μλ‘μ΄ μ¬νμ§κ° μμ΄μ.
λ€μ μ§λ¬Ένμκ² μ΄μ?",
sender="bot",
chat_container=chat_container,
key="region_empty"
)
# 3.2) μ¬μμ μ¬λΆ μΉ© λ²νΌ μΆλ ₯
restart_done_key = "region_restart_done"
chip_ph = st.empty()
if not st.session_state.get(restart_done_key, False):
with chip_ph:
choice = render_chip_buttons(
["μ π", "μλμ€ β"],
key_prefix="region_restart"
)
else:
choice = None
# 3.3) μμ§ μ무κ²λ μ ννμ§ μμ κ²½μ°
if choice is None:
return
chip_ph.empty()
st.session_state[restart_done_key] = True
# 3.4) μ¬μ©μ μ νκ° μΆλ ₯
log_and_render(
choice,
sender="user",
chat_container=chat_container,
key=f"user_restart_choice_{choice}"
)
# 3.5) μ¬μ©μκ° μ¬μΆμ²μ μνλ κ²½μ°
if choice == "μ π":
# μ¬ν μΆμ² μν μ΄κΈ°ν
for k in [region_key, prev_key, sample_key, restart_done_key]:
st.session_state.pop(k, None)
chip_ph.empty()
# λ€μ μΆμ² λ¨κ³λ‘ μ΄κΈ°ν
st.session_state["user_input_rendered"] = False
st.session_state["region_step"] = "restart"
log_and_render(
"λ€μ μ¬νμ§λ₯Ό μΆμ²ν΄λ릴κ²μ!
μμ¦ λ μ€λ₯΄λ μ¬νμ΄ μμΌμ κ°μ?",
sender="bot",
chat_container=chat_container,
key="region_restart_intro"
)
return
# 3.6) μ¬μ©μκ° μ’
λ£λ₯Ό μ νν κ²½μ°
else:
log_and_render("μ¬ν μΆμ²μ μ’
λ£ν κ²μ. νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
sender="bot",
chat_container=chat_container,
key="region_exit")
st.stop()
return
# ββββββββββββββββββ 4) μ¬νμ§ μμΈ λ¨κ³
if st.session_state[step_key] == "detail":
chosen = st.session_state[region_key]
# city μ΄λ¦ λ½μμ μΈμ
μ μ μ₯
row = travel_df[travel_df["μ¬νμ§"] == chosen].iloc[0]
st.session_state["selected_city"] = row["μ¬νλμ"]
st.session_state["selected_place"] = chosen
log_and_render(chosen,
sender="user",
chat_container=chat_container,
key=f"user_place_{chosen}")
handle_selected_place(
chosen,
travel_df,
external_score_df,
festival_df,
weather_df,
chat_container=chat_container
)
st.session_state[step_key] = "companion"
st.rerun()
return
# ββββββββββββββββββ 5) λνΒ·μ°λ Ή λ°κΈ° λ¨κ³
elif st.session_state[step_key] == "companion":
with chat_container:
# 5.1) μλ΄ λ©μμ§ μΆλ ₯
log_and_render(
"ν¨κ» κ°λ λΆμ΄λ μ°λ Ήλλ₯Ό μλ €μ£Όμλ©΄ λ λ± λ§λ μνμ 골λΌλ릴κ²μ!
"
"1οΈβ£ λν μ¬λΆ (νΌμ / μΉκ΅¬ / 컀ν / κ°μ‘± / λ¨μ²΄)
"
"2οΈβ£ μ°λ Ήλ (20λ / 30λ / 40λ / 50λ / 60λ μ΄μ)",
sender="bot",
chat_container=chat_container,
key="ask_companion_age"
)
# 5.1.1) λν 체ν¬λ°μ€
st.markdown(
'
π« λν μ ν
',
unsafe_allow_html=True
)
c_cols = st.columns(5)
comp_flags = {
"νΌμ": c_cols[0].checkbox("νΌμ"),
"μΉκ΅¬": c_cols[1].checkbox("μΉκ΅¬"),
"컀ν": c_cols[2].checkbox("컀ν"),
"κ°μ‘±": c_cols[3].checkbox("κ°μ‘±"),
"λ¨μ²΄": c_cols[4].checkbox("λ¨μ²΄"),
}
companions = [k for k, v in comp_flags.items() if v]
# 5.1.2) μ°λ Ή 체ν¬λ°μ€
st.markdown(
'π μ°λ Ή μ ν
',
unsafe_allow_html=True
)
a_cols = st.columns(5)
age_flags = {
"20λ": a_cols[0].checkbox("20λ"),
"30λ": a_cols[1].checkbox("30λ"),
"40λ": a_cols[2].checkbox("40λ"),
"50λ": a_cols[3].checkbox("50λ"),
"60λ μ΄μ": a_cols[4].checkbox("60λ μ΄μ"),
}
age_group = [k for k, v in age_flags.items() if v]
# 5.1.3) νμΈ λ²νΌ
confirm = st.button(
"μΆμ² λ°κΈ°",
key="btn_confirm_companion",
disabled=not (companions or age_group),
)
# 5.2) λ©μμ§ μΆλ ₯
if confirm:
# μ¬μ©μ λ²λΈ μΆλ ₯
user_msg = " / ".join(companions + age_group)
log_and_render(
user_msg if user_msg else "μ ν μ ν¨",
sender="user",
chat_container=chat_container,
key=f"user_comp_age_{random.randint(1,999999)}"
)
# μΈμ
μ μ₯
st.session_state["companions"] = companions or None
st.session_state["age_group"] = age_group or None
# λ€μ μ€ν
st.session_state[step_key] = "package"
st.rerun()
return
# ββββββββββββββββββ 6) λνΒ·μ°λ Ή νν°λ§Β· ν¨ν€μ§ μΆλ ₯ λ¨κ³
elif st.session_state[step_key] == "package":
# ν¨ν€μ§ λ²λΈμ μ΄λ―Έ λ§λ€μμΌλ©΄ 건λλ
if st.session_state.get("package_rendered", False):
st.session_state[step_key] = "package_end"
return
companions = st.session_state.get("companions")
age_group = st.session_state.get("age_group")
city = st.session_state.get("selected_city")
place = st.session_state.get("selected_place")
filtered = filter_packages_by_companion_age(
package_df, companions, age_group, city=city, top_n=2
)
if filtered.empty:
log_and_render(
"β οΈ μμ½μ§λ§ μ§κΈ 쑰건μ λ§λ ν¨ν€μ§κ° μμ΄μ.
"
"λ€λ₯Έ 쑰건μΌλ‘ λ€μ μ°Ύμλ³ΌκΉμ?",
sender="bot", chat_container=chat_container,
key="no_package"
)
st.session_state[step_key] = "companion" # λ€μ μ
λ ₯ λ¨κ³λ‘
st.rerun()
return
combo_msg = make_companion_age_message(companions, age_group)
header = f"{combo_msg}"
# ν¨ν€μ§ μΉ΄λ μΆλ ₯
used_phrases = set()
theme_row = travel_df[travel_df["μ¬νμ§"] == place]
raw_theme = theme_row["ν΅ν©ν
λ§λͺ
"].iloc[0] if not theme_row.empty else None
selected_ui_theme = theme_ui_map.get(raw_theme, (raw_theme,))[0]
title_candidates = theme_title_phrases.get(selected_ui_theme, ["μΆμ²"])
sampled_titles = random.sample(title_candidates,
k=min(2, len(title_candidates)))
# λ©μμ§ μμ±
pkg_msgs = [header]
for i, (_, row) in enumerate(filtered.iterrows(), 1):
desc, used_phrases = make_top2_description_custom(
row.to_dict(), used_phrases
)
tags = format_summary_tags_custom(row["μμ½μ 보"])
title_phrase = (sampled_titles[i-1] if i <= len(sampled_titles)
else random.choice(title_candidates))
title = f"{city} {title_phrase} ν¨ν€μ§"
url = row.URL
pkg_msgs.append(
f"{i}. {title}
"
f"π
Ό {desc}
{tags}
"
f''
'π λ°λ‘κ°κΈ° β'
)
# λ©μμ§ μΆλ ₯
log_and_render(
"
".join(pkg_msgs),
sender="bot",
chat_container=chat_container,
key=f"pkg_bundle_{random.randint(1,999999)}"
)
# μΈμ
μ 리
st.session_state["package_rendered"] = True
st.session_state[step_key] = "package_end"
return
# ββββββββββββββββββ 7) μ’
λ£ λ¨κ³
elif st.session_state[step_key] == "package_end":
log_and_render("νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
sender="bot", chat_container=chat_container,
key="goodbye")
# βββββββββββββββββββββββββββββββββββββ intent λͺ¨λ
def intent_ui(travel_df, external_score_df, festival_df, weather_df, package_df,
country_filter, city_filter, chat_container, intent, log_and_render):
"""intent(μλλ₯Ό μ
λ ₯νμ κ²½μ°) λͺ¨λ μ μ© UI & λ‘μ§"""
# ββββββββββββββββββ μΈμ
ν€ μ μ
sample_key = "intent_sample_df"
step_key = "intent_step"
prev_key = "intent_prev_places"
intent_key = "intent_chip_selected"
# ββββββββββββββββββ 0) μ΄κΈ°ν
if step_key not in st.session_state:
st.session_state[step_key] = "recommend_places"
st.session_state[prev_key] = set()
st.session_state.pop(sample_key, None)
# ββββββββββββββββββ 1) restart μνλ©΄ μΈνΈλ‘λ§ μΆλ ₯νκ³ μ’
λ£
if st.session_state[step_key] == "restart":
log_and_render(
"λ€μ μ¬νμ§λ₯Ό μΆμ²ν΄λ릴κ²μ!
μμ¦ λ μ€λ₯΄λ μ¬νμ΄ μμΌμ κ°μ?",
sender="bot",
chat_container=chat_container,
key="region_restart_intro"
)
return
# ββββββββββββββββββ 2) μ¬νμ§ μΆμ² λ¨κ³
if st.session_state[step_key] == "recommend_places":
selected_theme = intent
theme_df = recommend_places_by_theme(selected_theme, country_filter, city_filter)
theme_df = theme_df.drop_duplicates(subset=["μ¬νλμ"])
theme_df = theme_df.drop_duplicates(subset=["μ¬νμ§"])
# 2.1) μ΄μ μΆμ² κΈ°λ‘ μΈν
prev = st.session_state.setdefault(prev_key, set())
# 2.2) μ΄λ―Έ μνμ΄ μλ€λ©΄ result_df μ¬μ¬μ©
if sample_key in st.session_state and not st.session_state[sample_key].empty:
result_df = st.session_state[sample_key]
else:
# 2.3) μλ‘μ΄ μΆμ² λμ νν°λ§
candidates = theme_df[~theme_df["μ¬νμ§"].isin(prev)]
# 2.4) νλ³΄κ° μλ€λ©΄ μ’
λ£
if candidates.empty:
st.session_state[step_key] = "recommend_places_end"
st.rerun()
return
# 2.5) μλ‘μ΄ μΆμ² μΆμΆ λ° μ μ₯
result_df = apply_weighted_score_filter(candidates)
st.session_state[sample_key] = result_df
# prevμ λ±λ‘νμ¬ μ€λ³΅ μΆμ² λ°©μ§
prev.update(result_df["μ¬νμ§"])
st.session_state[prev_key] = prev
# 2.6) μ€νλ λ¬Έμ₯ μμ±
opening_line = intent_opening_lines.get(selected_theme, f"'{selected_theme}' μ¬νμ§λ₯Ό μκ°ν κ²μ.")
opening_line = opening_line.format(len(result_df))
# 2.7) μΆμ² λ©μμ§ κ΅¬μ±
message = "
".join([
f"{i+1}. {row.μ¬νμ§} "
f"({row.μ¬νλλΌ}, {row.μ¬νλμ}) "
f"{getattr(row, 'νμ€μ€λͺ
', 'μ€λͺ
μ΄ μμ΅λλ€')}"
for i, row in enumerate(result_df.itertuples())
])
# 2.8) μ±λ΄ μΆλ ₯ + μΉ© λ²νΌ λ λλ§
with chat_container:
log_and_render(f"{opening_line}
{message}",
sender="bot",
chat_container=chat_container,
key=f"intent_recommendation_{random.randint(1,999999)}")
recommend_names = result_df["μ¬νμ§"].tolist()
prev_choice = st.session_state.get(intent_key, None)
choice = render_chip_buttons(
recommend_names + ["λ€λ₯Έ μ¬νμ§ λ³΄κΈ° π"],
key_prefix="intent_chip",
selected_value=prev_choice
)
# 2.9) μ ν μκ±°λ μ€λ³΅ μ νμ΄λ©΄ λκΈ°
if not choice or choice == prev_choice:
return
# μ ν κ²°κ³Ό μ²λ¦¬
if choice:
if choice == "λ€λ₯Έ μ¬νμ§ λ³΄κΈ° π":
log_and_render("λ€λ₯Έ μ¬νμ§ λ³΄κΈ° π",
sender="user",
chat_container=chat_container,
key=f"user_place_refresh_{random.randint(1,999999)}")
st.session_state.pop(sample_key, None)
st.rerun()
return
# μ μ μ νλ κ²½μ°
st.session_state[intent_key] = choice
st.session_state[step_key] = "detail"
st.session_state.chat_log.append(("user", choice))
# μ€μ λ‘ μ νλ μ¬νμ§λ§ prevμ κΈ°λ‘
match = result_df[result_df["μ¬νμ§"] == choice]
if not match.empty:
prev.add(choice)
st.session_state[prev_key] = prev
# μν νκΈ°
st.session_state.pop(sample_key, None)
st.rerun()
return
# ββββββββββββββββββ 3) μΆμ² μ’
λ£ λ¨κ³
elif st.session_state[step_key] == "recommend_places_end":
# 3.1) λ©μμ§ μΆλ ₯
with chat_container:
log_and_render(
"β οΈ λ μ΄μ μλ‘μ΄ μ¬νμ§κ° μμ΄μ.
λ€μ μ§λ¬Ένμκ² μ΄μ?",
sender="bot",
chat_container=chat_container,
key="intent_empty"
)
# 3.2) μ¬μμ μ¬λΆ μΉ© λ²νΌ μΆλ ₯
restart_done_key = "intent_restart_done"
chip_ph = st.empty()
if not st.session_state.get(restart_done_key, False):
with chip_ph:
choice = render_chip_buttons(
["μ π", "μλμ€ β"],
key_prefix="intent_restart")
else:
choice = None
# 3.3) μμ§ μ무κ²λ μ ννμ§ μμ κ²½μ°
if choice is None:
return
chip_ph.empty()
st.session_state[restart_done_key] = True
# 3.4) μ¬μ©μ μ νκ° μΆλ ₯
log_and_render(choice,
sender="user",
chat_container=chat_container
)
# 3.5) μ¬μ©μκ° μ¬μΆμ²μ μνλ κ²½μ°
if choice == "μ π":
for k in [sample_key, prev_key, intent_key, restart_done_key]:
st.session_state.pop(k, None)
chip_ph.empty()
# λ€μ μΆμ² λ¨κ³λ‘ μ΄κΈ°ν
st.session_state["user_input_rendered"] = False
st.session_state["intent_step"] = "restart"
log_and_render(
"λ€μ μ¬νμ§λ₯Ό μΆμ²ν΄λ릴κ²μ!
μμ¦ λ μ€λ₯΄λ μ¬νμ΄ μμΌμ κ°μ?",
sender="bot",
chat_container=chat_container,
key="intent_restart_intro"
)
return
# 3.6) μ¬μ©μκ° μ’
λ£λ₯Ό μ νν κ²½μ°
else:
log_and_render("μ¬ν μΆμ²μ μ’
λ£ν κ²μ. νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
sender="bot",
chat_container=chat_container,
key="intent_exit")
st.stop()
return
# ββββββββββββββββββ 4) μ¬νμ§ μμΈ λ¨κ³
if st.session_state[step_key] == "detail":
chosen = st.session_state[intent_key]
# city μ΄λ¦ λ½μμ μΈμ
μ μ μ₯
row = travel_df[travel_df["μ¬νμ§"] == chosen].iloc[0]
st.session_state["selected_city"] = row["μ¬νλμ"]
st.session_state["selected_place"] = chosen
log_and_render(chosen,
sender="user",
chat_container=chat_container,
key=f"user_place_{chosen}")
handle_selected_place(
chosen,
travel_df,
external_score_df,
festival_df,
weather_df,
chat_container=chat_container
)
st.session_state[step_key] = "companion"
st.rerun()
return
# ββββββββββββββββββ 5) λνΒ·μ°λ Ή λ°κΈ° λ¨κ³
elif st.session_state[step_key] == "companion":
with chat_container:
# 5.1) μλ΄ λ©μμ§ μΆλ ₯
log_and_render(
"ν¨κ» κ°λ λΆμ΄λ μ°λ Ήλλ₯Ό μλ €μ£Όμλ©΄ λ λ± λ§λ μνμ 골λΌλ릴κ²μ!
"
"1οΈβ£ λν μ¬λΆ (νΌμ / μΉκ΅¬ / 컀ν / κ°μ‘± / λ¨μ²΄)
"
"2οΈβ£ μ°λ Ήλ (20λ / 30λ / 40λ / 50λ / 60λ μ΄μ)",
sender="bot",
chat_container=chat_container,
key="ask_companion_age"
)
# 5.1.1) λν 체ν¬λ°μ€
st.markdown(
'π« λν μ ν
',
unsafe_allow_html=True
)
c_cols = st.columns(5)
comp_flags = {
"νΌμ": c_cols[0].checkbox("νΌμ"),
"μΉκ΅¬": c_cols[1].checkbox("μΉκ΅¬"),
"컀ν": c_cols[2].checkbox("컀ν"),
"κ°μ‘±": c_cols[3].checkbox("κ°μ‘±"),
"λ¨μ²΄": c_cols[4].checkbox("λ¨μ²΄"),
}
companions = [k for k, v in comp_flags.items() if v]
# 5.1.2) μ°λ Ή 체ν¬λ°μ€
st.markdown(
'π μ°λ Ή μ ν
',
unsafe_allow_html=True
)
a_cols = st.columns(5)
age_flags = {
"20λ": a_cols[0].checkbox("20λ"),
"30λ": a_cols[1].checkbox("30λ"),
"40λ": a_cols[2].checkbox("40λ"),
"50λ": a_cols[3].checkbox("50λ"),
"60λ μ΄μ": a_cols[4].checkbox("60λ μ΄μ"),
}
age_group = [k for k, v in age_flags.items() if v]
# 5.1.3) νμΈ λ²νΌ
confirm = st.button(
"μΆμ² λ°κΈ°",
key="btn_confirm_companion",
disabled=not (companions or age_group),
)
# 5.2) λ©μμ§ μΆλ ₯
if confirm:
# μ¬μ©μ λ²λΈ μΆλ ₯
user_msg = " / ".join(companions + age_group)
log_and_render(
user_msg if user_msg else "μ ν μ ν¨",
sender="user",
chat_container=chat_container,
key=f"user_comp_age_{random.randint(1,999999)}"
)
# μΈμ
μ μ₯
st.session_state["companions"] = companions or None
st.session_state["age_group"] = age_group or None
# λ€μ μ€ν
st.session_state[step_key] = "package"
st.rerun()
return
# ββββββββββββββββββ 6) λνΒ·μ°λ Ή νν°λ§Β· ν¨ν€μ§ μΆλ ₯ λ¨κ³
elif st.session_state[step_key] == "package":
# ν¨ν€μ§ λ²λΈμ μ΄λ―Έ λ§λ€μμΌλ©΄ 건λλ
if st.session_state.get("package_rendered", False):
st.session_state[step_key] = "package_end"
return
companions = st.session_state.get("companions")
age_group = st.session_state.get("age_group")
city = st.session_state.get("selected_city")
place = st.session_state.get("selected_place")
filtered = filter_packages_by_companion_age(
package_df, companions, age_group, city=city, top_n=2
)
if filtered.empty:
log_and_render(
"β οΈ μμ½μ§λ§ μ§κΈ 쑰건μ λ§λ ν¨ν€μ§κ° μμ΄μ.
"
"λ€λ₯Έ 쑰건μΌλ‘ λ€μ μ°Ύμλ³ΌκΉμ?",
sender="bot", chat_container=chat_container,
key="no_package"
)
st.session_state[step_key] = "companion" # λ€μ μ
λ ₯ λ¨κ³λ‘
st.rerun()
return
combo_msg = make_companion_age_message(companions, age_group)
header = f"{combo_msg}"
# ν¨ν€μ§ μΉ΄λ μΆλ ₯
used_phrases = set()
theme_row = travel_df[travel_df["μ¬νμ§"] == place]
raw_theme = theme_row["ν΅ν©ν
λ§λͺ
"].iloc[0] if not theme_row.empty else None
selected_ui_theme = theme_ui_map.get(raw_theme, (raw_theme,))[0]
title_candidates = theme_title_phrases.get(selected_ui_theme, ["μΆμ²"])
sampled_titles = random.sample(title_candidates,
k=min(2, len(title_candidates)))
# λ©μμ§ μμ±
pkg_msgs = [header]
for i, (_, row) in enumerate(filtered.iterrows(), 1):
desc, used_phrases = make_top2_description_custom(
row.to_dict(), used_phrases
)
tags = format_summary_tags_custom(row["μμ½μ 보"])
title_phrase = (sampled_titles[i-1] if i <= len(sampled_titles)
else random.choice(title_candidates))
title = f"{city} {title_phrase} ν¨ν€μ§"
url = row.URL
pkg_msgs.append(
f"{i}. {title}
"
f"π
Ό {desc}
{tags}
"
f''
'π λ°λ‘κ°κΈ° β'
)
# λ©μμ§ μΆλ ₯
log_and_render(
"
".join(pkg_msgs),
sender="bot",
chat_container=chat_container,
key=f"pkg_bundle_{random.randint(1,999999)}"
)
# μΈμ
μ 리
st.session_state["package_rendered"] = True
st.session_state[step_key] = "package_end"
return
# ββββββββββββββββββ 7) μ’
λ£ λ¨κ³
elif st.session_state[step_key] == "package_end":
log_and_render("νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
sender="bot", chat_container=chat_container,
key="goodbye")
# βββββββββββββββββββββββββββββββββββββ emotion λͺ¨λ
def emotion_ui(travel_df, external_score_df, festival_df, weather_df, package_df,
country_filter, city_filter, chat_container, candidate_themes,
intent, emotion_groups, top_emotions, log_and_render):
"""emotion(κ°μ μ μ
λ ₯νμ κ²½μ°) λͺ¨λ μ μ© UI & λ‘μ§"""
# ββββββββββββββββββ μΈμ
ν€ μ μ
sample_key = "emotion_sample_df"
step_key = "emotion_step"
theme_key = "selected_theme"
emotion_key = "emotion_chip_selected"
prev_key = "emotion_prev_places"
# ββββββββββββββββββ 0) μ΄κΈ°ν
if step_key not in st.session_state:
st.session_state[step_key] = "theme_selection"
st.session_state[prev_key] = set()
st.session_state.pop(sample_key, None)
# ββββββββββββββββββ 1) restart μνλ©΄ μΈνΈλ‘λ§ μΆλ ₯νκ³ μ’
λ£
if st.session_state[step_key] == "restart":
log_and_render(
"λ€μ μ¬νμ§λ₯Ό μΆμ²ν΄λ릴κ²μ!
μμ¦ λ μ€λ₯΄λ μ¬νμ΄ μμΌμ κ°μ?",
sender="bot",
chat_container=chat_container,
key="region_restart_intro"
)
return
# ββββββββββββββββββ 2) ν
λ§ μΆμ² λ¨κ³
if st.session_state[step_key] == "theme_selection":
# μΆμ² ν
λ§ 1κ°μΌ κ²½μ°
if len(candidate_themes) == 1:
selected_theme = candidate_themes[0]
st.session_state[theme_key] = selected_theme
log_and_render(f"μΆμ² κ°λ₯ν ν
λ§κ° 1κ°μ΄λ―λ‘ '{selected_theme}'μ μ νν κ²μ.", sender="bot", chat_container=chat_container)
st.session_state[step_key] = "recommend_places"
st.rerun()
# ν
λ§κ° μ¬λ¬ κ°μΌ κ²½μ°
else:
# μΈνΈλ‘ λ©μμ§
intro_msg = generate_intro_message(intent=intent, emotion_groups=emotion_groups, emotion_scores=top_emotions)
log_and_render(f"{intro_msg}
μλ μ€ λ§μμ΄ λ리λ μ¬ν μ€νμΌμ 골λΌμ£ΌμΈμ π«", sender="bot", chat_container=chat_container)
# ν보 ν
λ§ μ€λΉ
dfs = [recommend_places_by_theme(t, country_filter, city_filter) for t in candidate_themes]
dfs = [df for df in dfs if not df.empty]
all_theme_df = pd.concat(dfs) if dfs else pd.DataFrame(columns=travel_df.columns)
all_theme_df = all_theme_df.drop_duplicates(subset=["μ¬νμ§"])
all_theme_names = all_theme_df["ν΅ν©ν
λ§λͺ
"].dropna().tolist()
available_themes = []
for t in candidate_themes:
if t in all_theme_names and t not in available_themes:
available_themes.append(t)
for t in all_theme_names:
if t not in available_themes:
available_themes.append(t)
available_themes = available_themes[:3] # μ΅λ 3κ°
# μΉ© UI μΆλ ₯
with chat_container:
chip = render_chip_buttons(
[theme_ui_map.get(t, (t, ""))[0] for t in available_themes],
key_prefix="theme_chip"
)
# μ νμ΄ μλ£λλ©΄ λ€μ λ¨κ³λ‘ μ΄λ
if chip:
selected_theme = ui_to_theme_map.get(chip, chip)
st.session_state[theme_key] = selected_theme
st.session_state[step_key] = "recommend_places"
st.session_state["emotion_all_theme_df"] = all_theme_df
log_and_render(f"{chip}", sender="user",
chat_container=chat_container)
st.rerun()
# ββββββββββββββββββ 3) μ¬νμ§ μΆμ² λ¨κ³
if st.session_state[step_key] == "recommend_places":
all_theme_df = st.session_state.get("emotion_all_theme_df", pd.DataFrame())
selected_theme = st.session_state.get(theme_key, "")
prev_key = "emotion_prev_places"
prev = st.session_state.setdefault(prev_key, set())
# μμΈ μ²λ¦¬: λ°μ΄ν° μμ κ²½μ°
if all_theme_df.empty or not selected_theme:
log_and_render("μΆμ² λ°μ΄ν°λ₯Ό λΆλ¬μ€λ λ° λ¬Έμ κ° λ°μνμ΄μ.
λ€μ μ
λ ₯ν΄ μ£ΌμΈμ.", sender="bot", chat_container=chat_container)
return
if sample_key not in st.session_state:
theme_df = all_theme_df[all_theme_df["ν΅ν©ν
λ§λͺ
"] == selected_theme]
theme_df = theme_df.drop_duplicates(subset=["μ¬νλμ"])
theme_df = theme_df.drop_duplicates(subset=["μ¬νμ§"])
remaining = theme_df[~theme_df["μ¬νμ§"].isin(prev)]
if remaining.empty:
st.session_state[step_key] = "recommend_places_end"
st.rerun()
return
result_df = apply_weighted_score_filter(remaining)
st.session_state[sample_key] = result_df
else:
result_df = st.session_state[sample_key]
# μΆμ² μ λΆμ‘±ν κ²½μ° Fallback 보μ
if len(result_df) < 3:
fallback = travel_df[
(travel_df["ν΅ν©ν
λ§λͺ
"] == selected_theme) &
(~travel_df["μ¬νμ§"].isin(result_df["μ¬νμ§"]))
].drop_duplicates(subset=["μ¬νμ§"])
if not fallback.empty:
fill_count = min(3 - len(result_df), len(fallback))
fill = fallback.sample(n=fill_count, random_state=random.randint(1, 9999))
result_df = pd.concat([result_df, fill], ignore_index=True)
# μν μ μ₯
st.session_state[sample_key] = result_df
# 2.1)첫 λ¬Έμ₯ μΆλ ₯
ui_name = theme_ui_map.get(selected_theme, (selected_theme,))[0]
opening_line_template = theme_opening_lines.get(ui_name)
opening_line = opening_line_template.format(len(result_df)) if opening_line_template else ""
message = (
"
".join([
f"{i+1}. {row.μ¬νμ§} "
f"({row.μ¬νλλΌ}, {row.μ¬νλμ}) "
f"{getattr(row, 'νμ€μ€λͺ
', 'μ€λͺ
μ΄ μμ΅λλ€')}"
for i, row in enumerate(result_df.itertuples())
])
)
if opening_line_template:
message_combined = f"{opening_line}
{message}"
with chat_container:
log_and_render(message_combined,
sender="bot",
chat_container=chat_container,
key=f"emotion_recommendation_{random.randint(1,999999)}"
)
# 2.2) μΉ© λ²νΌμΌλ‘ μΆμ²μ§ μ€ μ νλ°κΈ°
recommend_names = result_df["μ¬νμ§"].tolist()
prev_choice = st.session_state.get(emotion_key, None)
choice = render_chip_buttons(
recommend_names + ["λ€λ₯Έ μ¬νμ§ λ³΄κΈ° π"],
key_prefix="emotion_chip",
selected_value=prev_choice
)
# 2.3) μ ν κ²°κ³Ό μ²λ¦¬
if not choice or choice == prev_choice:
return
if choice == "λ€λ₯Έ μ¬νμ§ λ³΄κΈ° π":
log_and_render("λ€λ₯Έ μ¬νμ§ λ³΄κΈ° π",
sender="user",
chat_container=chat_container,
key=f"user_place_refresh_{random.randint(1,999999)}")
st.session_state.pop(sample_key, None)
st.rerun()
return
# μ€μ μ νν μ¬νμ§ μ²λ¦¬
st.session_state[emotion_key] = choice
st.session_state[step_key] = "detail"
st.session_state.chat_log.append(("user", choice))
# μ νν μ¬νμ§λ₯Ό prev κΈ°λ‘μ μΆκ°
match = result_df[result_df["μ¬νμ§"] == choice]
if not match.empty:
prev.add(choice)
st.session_state[prev_key] = prev
# μν νκΈ°
st.session_state.pop(sample_key, None)
st.rerun()
return
# ββββββββββββββββββ 3) μΆμ² μ’
λ£ λ¨κ³: λ μ΄μ μΆμ²ν μ¬νμ§κ° μμ λ
elif st.session_state[step_key] == "recommend_place_end":
with chat_container:
# 3.1) λ©μμ§ μΆλ ₯
log_and_render(
"β οΈ λ μ΄μ μλ‘μ΄ μ¬νμ§κ° μμ΄μ.
λ€μ μ§λ¬Ένμκ² μ΄μ?",
sender="bot",
chat_container=chat_container,
key="emotion_empty"
)
# 3.2) μ¬μμ μ¬λΆ μΉ© λ²νΌ μΆλ ₯
restart_done_key = "emotion_restart_done"
chip_ph = st.empty()
if not st.session_state.get(restart_done_key, False):
with chip_ph:
choice = render_chip_buttons(
["μ π", "μλμ€ β"],
key_prefix="emotion_restart"
)
else:
choice = None
# 3.3) μμ§ μ무κ²λ μ ννμ§ μμ κ²½μ°
if choice is None:
return
chip_ph.empty()
st.session_state[restart_done_key] = True
# 3.4) μ¬μ©μ μ νκ° μΆλ ₯
log_and_render(
choice,
sender="user",
chat_container=chat_container,
key=f"user_restart_choice_{choice}"
)
# 3.5) μ¬μ©μκ° μ¬μΆμ²μ μνλ κ²½μ°
if choice == "μ π":
# μ¬ν μΆμ² μν μ΄κΈ°ν
for k in [emotion_key, prev_key, sample_key, restart_done_key]:
st.session_state.pop(k, None)
chip_ph.empty()
# λ€μ μΆμ² λ¨κ³λ‘ μ΄κΈ°ν
st.session_state["user_input_rendered"] = False
st.session_state["emotion_step"] = "restart"
log_and_render(
"λ€μ μ¬νμ§λ₯Ό μΆμ²ν΄λ릴κ²μ!
μμ¦ λ μ€λ₯΄λ μ¬νμ΄ μμΌμ κ°μ?",
sender="bot",
chat_container=chat_container,
key="emotion_restart_intro"
)
return
# 3.6) μ¬μ©μκ° μ’
λ£λ₯Ό μ νν κ²½μ°
else:
log_and_render("μ¬ν μΆμ²μ μ’
λ£ν κ²μ. νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
sender="bot",
chat_container=chat_container,
key="emotion_exit")
st.stop()
return
# ββββββββββββββββββ 4) μ¬νμ§ μμΈ λ¨κ³
if st.session_state[step_key] == "detail":
chosen = st.session_state[emotion_key]
# city μ΄λ¦ λ½μμ μΈμ
μ μ μ₯
row = travel_df[travel_df["μ¬νμ§"] == chosen].iloc[0]
st.session_state["selected_city"] = row["μ¬νλμ"]
st.session_state["selected_place"] = chosen
log_and_render(chosen,
sender="user",
chat_container=chat_container,
key=f"user_place_{chosen}")
handle_selected_place(
chosen,
travel_df,
external_score_df,
festival_df,
weather_df,
chat_container=chat_container
)
st.session_state[step_key] = "companion"
st.rerun()
return
# ββββββββββββββββββ 5) λνΒ·μ°λ Ή λ°κΈ° λ¨κ³
elif st.session_state[step_key] == "companion":
with chat_container:
# 5.1) μλ΄ λ©μμ§ μΆλ ₯
log_and_render(
"ν¨κ» κ°λ λΆμ΄λ μ°λ Ήλλ₯Ό μλ €μ£Όμλ©΄ λ λ± λ§λ μνμ 골λΌλ릴κ²μ!
"
"1οΈβ£ λν μ¬λΆ (νΌμ / μΉκ΅¬ / 컀ν / κ°μ‘± / λ¨μ²΄)
"
"2οΈβ£ μ°λ Ήλ (20λ / 30λ / 40λ / 50λ / 60λ μ΄μ)",
sender="bot",
chat_container=chat_container,
key="ask_companion_age"
)
# 5.1.1) λν 체ν¬λ°μ€
st.markdown(
'π« λν μ ν
',
unsafe_allow_html=True
)
c_cols = st.columns(5)
comp_flags = {
"νΌμ": c_cols[0].checkbox("νΌμ"),
"μΉκ΅¬": c_cols[1].checkbox("μΉκ΅¬"),
"컀ν": c_cols[2].checkbox("컀ν"),
"κ°μ‘±": c_cols[3].checkbox("κ°μ‘±"),
"λ¨μ²΄": c_cols[4].checkbox("λ¨μ²΄"),
}
companions = [k for k, v in comp_flags.items() if v]
# 5.1.2) μ°λ Ή 체ν¬λ°μ€
st.markdown(
'π μ°λ Ή μ ν
',
unsafe_allow_html=True
)
a_cols = st.columns(5)
age_flags = {
"20λ": a_cols[0].checkbox("20λ"),
"30λ": a_cols[1].checkbox("30λ"),
"40λ": a_cols[2].checkbox("40λ"),
"50λ": a_cols[3].checkbox("50λ"),
"60λ μ΄μ": a_cols[4].checkbox("60λ μ΄μ"),
}
age_group = [k for k, v in age_flags.items() if v]
# 5.1.3) νμΈ λ²νΌ
confirm = st.button(
"μΆμ² λ°κΈ°",
key="btn_confirm_companion",
disabled=not (companions or age_group),
)
# 5.2) λ©μμ§ μΆλ ₯
if confirm:
# μ¬μ©μ λ²λΈ μΆλ ₯
user_msg = " / ".join(companions + age_group)
log_and_render(
user_msg if user_msg else "μ ν μ ν¨",
sender="user",
chat_container=chat_container,
key=f"user_comp_age_{random.randint(1,999999)}"
)
# μΈμ
μ μ₯
st.session_state["companions"] = companions or None
st.session_state["age_group"] = age_group or None
# λ€μ μ€ν
st.session_state[step_key] = "package"
st.rerun()
return
# ββββββββββββββββββ 6) λνΒ·μ°λ Ή νν°λ§Β· ν¨ν€μ§ μΆλ ₯ λ¨κ³
elif st.session_state[step_key] == "package":
# ν¨ν€μ§ λ²λΈμ μ΄λ―Έ λ§λ€μμΌλ©΄ 건λλ
if st.session_state.get("package_rendered", False):
st.session_state[step_key] = "package_end"
return
companions = st.session_state.get("companions")
age_group = st.session_state.get("age_group")
city = st.session_state.get("selected_city")
place = st.session_state.get("selected_place")
filtered = filter_packages_by_companion_age(
package_df, companions, age_group, city=city, top_n=2
)
if filtered.empty:
log_and_render(
"β οΈ μμ½μ§λ§ μ§κΈ 쑰건μ λ§λ ν¨ν€μ§κ° μμ΄μ.
"
"λ€λ₯Έ 쑰건μΌλ‘ λ€μ μ°Ύμλ³ΌκΉμ?",
sender="bot", chat_container=chat_container,
key="no_package"
)
st.session_state[step_key] = "companion"
st.rerun()
return
combo_msg = make_companion_age_message(companions, age_group)
header = f"{combo_msg}"
# ν¨ν€μ§ μΉ΄λ μΆλ ₯
used_phrases = set()
theme_row = travel_df[travel_df["μ¬νμ§"] == place]
raw_theme = theme_row["ν΅ν©ν
λ§λͺ
"].iloc[0] if not theme_row.empty else None
selected_ui_theme = theme_ui_map.get(raw_theme, (raw_theme,))[0]
title_candidates = theme_title_phrases.get(selected_ui_theme, ["μΆμ²"])
sampled_titles = random.sample(title_candidates,
k=min(2, len(title_candidates)))
# λ©μμ§ μμ±
pkg_msgs = [header]
for i, (_, row) in enumerate(filtered.iterrows(), 1):
desc, used_phrases = make_top2_description_custom(
row.to_dict(), used_phrases
)
tags = format_summary_tags_custom(row["μμ½μ 보"])
title_phrase = (sampled_titles[i-1] if i <= len(sampled_titles)
else random.choice(title_candidates))
title = f"{city} {title_phrase} ν¨ν€μ§"
url = row.URL
pkg_msgs.append(
f"{i}. {title}
"
f"π
Ό {desc}
{tags}
"
f''
'π λ°λ‘κ°κΈ° β'
)
# λ©μμ§ μΆλ ₯
log_and_render(
"
".join(pkg_msgs),
sender="bot",
chat_container=chat_container,
key=f"pkg_bundle_{random.randint(1,999999)}"
)
# μΈμ
μ 리
st.session_state["package_rendered"] = True
st.session_state[step_key] = "package_end"
return
# ββββββββββββββββββ 7) μ’
λ£ λ¨κ³
elif st.session_state[step_key] == "package_end":
log_and_render("νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
sender="bot", chat_container=chat_container,
key="goodbye")
# βββββββββββββββββββββββββββββββββββββ unknown λͺ¨λ
def unknown_ui(country, city, chat_container, log_and_render):
"""unknown λͺ¨λ(μμ§ DBμ μλ λλΌΒ·λμμΌ λ μλ΄) μ μ© UI & λ‘μ§"""
# μλ΄ λ©μμ§
if city:
msg = (f"π μ£μ‘ν΄μ. ν΄λΉ {city}μ μ¬νμ§λ "
"μμ§ μ€λΉ μ€μ΄μμ.
λΉ λ₯Έ μμΌ μμ μ
λ°μ΄νΈν κ²μ!")
elif country:
msg = (f"π μ£μ‘ν΄μ. ν΄λΉ {country}μ μ¬νμ§λ "
"μμ§ μ€λΉ μ€μ΄μμ.
λΉ λ₯Έ μμΌ μμ μ
λ°μ΄νΈν κ²μ!")
else:
msg = "π μ£μ‘ν΄μ. ν΄λΉ μ¬νμ§λ μμ§ μ€λΉ μ€μ΄μμ."
with chat_container:
log_and_render(
f"{msg}",
sender="bot",
chat_container=chat_container,
key="unknown_dest"
)
# βββββββββββββββββββββββββββββββββββββ μ±λ΄ νΈμΆ
def main():
init_session()
chat_container = st.container()
if "chat_log" in st.session_state and st.session_state.chat_log:
replay_log(chat_container)
# βββββ greeting λ©μμ§ μΆλ ₯
if not st.session_state.get("greeting_rendered", False):
greeting_message = (
"μλ
νμΈμ. λͺ¨μ(MoAi)μ
λλ€.π€
"
"μμ¦ μ΄λ€ μ¬νμ΄ λ μ€λ₯΄μΈμ?
""λͺ¨μκ° λ± λ§λ μ¬νμ§λ₯Ό μ°Ύμλ릴κ²μ."
)
log_and_render(
greeting_message,
sender="bot",
chat_container=chat_container,
key="greeting"
)
st.session_state["greeting_rendered"] = True
# βββββ μ¬μ©μ μ
λ ₯ & μΆμ² μμ
# 1) μ¬μ©μ μ
λ ₯
user_input = st.text_input(
"μ
λ ₯μ°½", # λΉμ΄μμ§ μμ λΌλ²¨(μ κ·Όμ± ν보)
placeholder="ex)'μμ¦ νλ§μ΄ νμν΄μ', 'κ°μ‘± μ¬ν μ΄λκ° μ’μκΉμ?'",
key="user_input",
label_visibility="collapsed", # νλ©΄μμ μ¨κΉ
)
user_input_key = "last_user_input"
select_keys = ["intent_chip_selected", "region_chip_selected", "emotion_chip_selected", "theme_chip_selected"]
# 1-1) βμ§μ§ μλ‘ μ
λ ₯β κ°μ§
prev = st.session_state.get(user_input_key, "")
if user_input and user_input != prev:
for k in select_keys:
st.session_state.pop(k, None)
st.session_state[user_input_key] = user_input
st.session_state["user_input_rendered"] = False
# step μ΄κΈ°ν
st.session_state["region_step"] = "recommend"
st.rerun()
# 1-2) μ¬μ©μ λ©μμ§ ν λ²λ§ λ λλ§
if user_input and not st.session_state.get("user_input_rendered", False):
log_and_render(
user_input,
sender="user",
chat_container = chat_container,
key=f"user_input_{user_input}"
)
st.session_state["user_input_rendered"] = True
if user_input:
# 2) mode νμ§
_, _, mode = detect_location_filter(user_input)
top_emotions, emotion_groups = analyze_emotion(user_input)
intent, intent_score = detect_intent(user_input)
country_filter, city_filter, _ = detect_location_filter(user_input)
candidate_themes = extract_themes(
emotion_groups,
intent,
force_mode=(intent_score >= 0.70)
)
if intent_score >= 0.70:
mode = "intent"
# π DEBUG ββββββββββββββββββββββββββββββββ
# with st.expander("π DEBUG - λͺ¨λ νμ ", expanded=True):
# st.markdown(f"""
# **μ
λ ₯ λ¬Έμ₯**: `{user_input}`
# **detect_location_filter** π
# β’ country β `{country_filter}`
# β’ city β `{city_filter}`
# β’ mode β `{mode}`
# **intent_score**: `{intent_score:.3f}`
# **top_emotions**: `{top_emotions}`
# """)
# ββββββββββββββββββββββββββββββββββββββββ
# 3) λͺ¨λλ³ λΆκΈ°
if mode == "region":
region_ui(
travel_df,
external_score_df,
festival_df,
weather_df,
package_df,
country_filter,
city_filter,
chat_container,
log_and_render
)
return
elif mode == "intent":
intent_ui(
travel_df,
external_score_df,
festival_df,
weather_df,
package_df,
country_filter,
city_filter,
chat_container,
intent,
log_and_render)
return
elif mode == "unknown":
unknown_ui(
country_filter,
city_filter,
chat_container,
log_and_render)
return
else:
emotion_ui(
travel_df,
external_score_df,
festival_df,
weather_df,
package_df,
country_filter,
city_filter,
chat_container,
candidate_themes,
intent,
emotion_groups,
top_emotions,
log_and_render
)
if __name__ == "__main__":
main()
#cmd μ
λ ₯-> cd "νμΌ μμΉ κ²½λ‘ λ³΅λΆ"
#ex(C:\Users\gayoung\Desktop\multi\0514 - project\06 - streamlit ν
μ€νΈ\test)
#cmd μ
λ ₯ -> streamlit run app.py