Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,11 +3,14 @@ import streamlit.components.v1 as components
|
|
| 3 |
import json
|
| 4 |
import base64
|
| 5 |
import os
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
# ==========================================
|
| 8 |
-
# [ํ์] Groq API ํค
|
| 9 |
# ==========================================
|
| 10 |
-
GROQ_API_KEY = "gsk_..." # <-- ์ฌ๊ธฐ์ ๋ณธ์ธ ํค ์
๋ ฅ!
|
| 11 |
|
| 12 |
# 1. ํ์ด์ง ์ค์
|
| 13 |
st.set_page_config(page_title="Drunken Plane", page_icon="โ๏ธ", layout="wide")
|
|
@@ -20,14 +23,47 @@ st.markdown("""
|
|
| 20 |
</style>
|
| 21 |
""", unsafe_allow_html=True)
|
| 22 |
|
| 23 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
try:
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
except Exception as e:
|
|
|
|
|
|
|
| 28 |
PLACE_DB = {}
|
| 29 |
-
st.error(f"๐จ DB ๋ก๋ ์คํจ: {e}")
|
| 30 |
|
|
|
|
| 31 |
emily_base64 = ""
|
| 32 |
if os.path.exists('emily.jpg'):
|
| 33 |
try:
|
|
@@ -38,7 +74,7 @@ if os.path.exists('emily.jpg'):
|
|
| 38 |
db_json_raw = json.dumps(PLACE_DB, ensure_ascii=False)
|
| 39 |
db_base64 = base64.b64encode(db_json_raw.encode('utf-8')).decode('utf-8')
|
| 40 |
|
| 41 |
-
#
|
| 42 |
html_code = f"""
|
| 43 |
<!DOCTYPE html>
|
| 44 |
<html>
|
|
@@ -436,8 +472,8 @@ html_code = f"""
|
|
| 436 |
"<div class='pc-cat'>" + translatedCat + "</div>" +
|
| 437 |
"<div class='pc-desc'>" + desc + "</div>" +
|
| 438 |
"<div class='card-actions'>" +
|
| 439 |
-
"<button class='btn-action btn-ai' onclick=\
|
| 440 |
-
"<button class='btn-action btn-map' onclick=\
|
| 441 |
list.appendChild(card);
|
| 442 |
}});
|
| 443 |
}}
|
|
@@ -604,7 +640,7 @@ html_code = f"""
|
|
| 604 |
if(loadingMsg) loadingMsg.remove();
|
| 605 |
|
| 606 |
let finalMsg = res;
|
| 607 |
-
const match = res.match(/\
|
| 608 |
if(match) {{
|
| 609 |
const pName = match[1];
|
| 610 |
const found = nearbyPlaces.find(p => p.name.includes(pName));
|
|
@@ -643,8 +679,8 @@ html_code = f"""
|
|
| 643 |
|
| 644 |
const popupContent = "<div style='text-align:center'><b>" + p.name + "</b><br>" +
|
| 645 |
"<div style='display:flex; gap:5px; margin-top:5px; justify-content:center;'>" +
|
| 646 |
-
"<button onclick=\
|
| 647 |
-
"<button onclick=\
|
| 648 |
"</div></div>";
|
| 649 |
L.marker([p.lat, p.lon]).bindPopup(popupContent).addTo(worldMap);
|
| 650 |
}}
|
|
@@ -701,7 +737,7 @@ html_code = f"""
|
|
| 701 |
const targetLang = TRANSLATIONS[currentLang].ai;
|
| 702 |
const detailMessages = [
|
| 703 |
{{ role: "system", content: "Act as Emily. Write a DETAILED, structured travel report." }},
|
| 704 |
-
{{ role: "user", content: "Write a report for '" + n + "' in '" + c + "'. Include sections: 'Vibe & Atmosphere', 'Must Try Drinks/Food', 'Secret Tip'. Use Markdown. IMPORTANT: Write this report IN " + targetLang + " ONLY." }}
|
| 705 |
];
|
| 706 |
|
| 707 |
const res = await callGroq(detailMessages);
|
|
|
|
| 3 |
import json
|
| 4 |
import base64
|
| 5 |
import os
|
| 6 |
+
import pandas as pd
|
| 7 |
+
import requests
|
| 8 |
+
import io
|
| 9 |
|
| 10 |
# ==========================================
|
| 11 |
+
# [ํ์] Groq API ํค
|
| 12 |
# ==========================================
|
| 13 |
+
GROQ_API_KEY = "gsk_..." # <-- ์ฌ๊ธฐ์ ๋ณธ์ธ ํค ์
๋ ฅ! (ํน์ Secrets์์ ๊ฐ์ ธ์๋ ๋จ)
|
| 14 |
|
| 15 |
# 1. ํ์ด์ง ์ค์
|
| 16 |
st.set_page_config(page_title="Drunken Plane", page_icon="โ๏ธ", layout="wide")
|
|
|
|
| 23 |
</style>
|
| 24 |
""", unsafe_allow_html=True)
|
| 25 |
|
| 26 |
+
# ==========================================
|
| 27 |
+
# 2. ๋ฐ์ดํฐ ๋ก๋ (๊ตฌ๊ธ ์ํธ ์ฐ๋)
|
| 28 |
+
# ==========================================
|
| 29 |
+
PLACE_DB = {}
|
| 30 |
+
|
| 31 |
+
# (1) ์ํฌ๋ฆฟ์์ ๊ตฌ๊ธ ์ํธ ๋งํฌ ๊ฐ์ ธ์ค๊ธฐ
|
| 32 |
+
SHEET_URL = os.environ.get("PRIVATE_SHEET_URL")
|
| 33 |
+
|
| 34 |
try:
|
| 35 |
+
if SHEET_URL:
|
| 36 |
+
# ๊ตฌ๊ธ ์ํธ(CSV) ๋ค์ด๋ก๋
|
| 37 |
+
response = requests.get(SHEET_URL)
|
| 38 |
+
response.raise_for_status()
|
| 39 |
+
|
| 40 |
+
# CSV๋ฅผ ๋ฐ์ดํฐํ๋ ์์ผ๋ก ๋ณํ
|
| 41 |
+
df = pd.read_csv(io.StringIO(response.text))
|
| 42 |
+
|
| 43 |
+
# ๋ฐ์ดํฐ๋ฅผ ๊ตญ๊ฐ๋ณ๋ก ๊ทธ๋ฃนํํ์ฌ JSON ๊ตฌ์กฐ(PLACE_DB)๋ก ๋ณํ
|
| 44 |
+
# ๊ตฌ์กฐ: { "CountryName": [ {place_info}, {place_info}... ] }
|
| 45 |
+
if 'Country' in df.columns:
|
| 46 |
+
for country, group in df.groupby('Country'):
|
| 47 |
+
# NaN ๊ฐ(๋น์นธ)์ ๋น ๋ฌธ์์ด๋ก ์ฒ๋ฆฌ (์๋ฌ ๋ฐฉ์ง)
|
| 48 |
+
cleaned_group = group.fillna("").to_dict(orient='records')
|
| 49 |
+
PLACE_DB[country] = cleaned_group
|
| 50 |
+
else:
|
| 51 |
+
st.error("๐จ CSV ํ์ผ์ 'Country' ์ปฌ๋ผ์ด ์์ต๋๋ค.")
|
| 52 |
+
|
| 53 |
+
else:
|
| 54 |
+
# ์ํฌ๋ฆฟ์ด ์์ผ๋ฉด ๋ก์ปฌ ํ์ผ ์๋ (๋ฐฑ์
์ฉ)
|
| 55 |
+
if os.path.exists('places.json'):
|
| 56 |
+
with open('places.json', 'r', encoding='utf-8-sig') as f:
|
| 57 |
+
PLACE_DB = json.load(f)
|
| 58 |
+
else:
|
| 59 |
+
st.warning("โ ๏ธ ๋ฐ์ดํฐ ์์ค๊ฐ ์์ต๋๋ค. (Secrets ์ค์ ํ์)")
|
| 60 |
+
|
| 61 |
except Exception as e:
|
| 62 |
+
st.error(f"๐จ ๋ฐ์ดํฐ ๋ก๋ ์ค ์ค๋ฅ ๋ฐ์: {e}")
|
| 63 |
+
# ์คํจ ์ ๋น DB๋ผ๋ ์์ฑํด์ ์ฑ์ด ๊บผ์ง๋ ๊ฑด ๋ฐฉ์ง
|
| 64 |
PLACE_DB = {}
|
|
|
|
| 65 |
|
| 66 |
+
# 3. ์ด๋ฏธ์ง ๋ฐ ๋ฐ์ดํฐ ์ธ์ฝ๋ฉ
|
| 67 |
emily_base64 = ""
|
| 68 |
if os.path.exists('emily.jpg'):
|
| 69 |
try:
|
|
|
|
| 74 |
db_json_raw = json.dumps(PLACE_DB, ensure_ascii=False)
|
| 75 |
db_base64 = base64.b64encode(db_json_raw.encode('utf-8')).decode('utf-8')
|
| 76 |
|
| 77 |
+
# 4. HTML/JS/CSS (๊ธฐ์กด ์ฝ๋ 100% ์ ์ง)
|
| 78 |
html_code = f"""
|
| 79 |
<!DOCTYPE html>
|
| 80 |
<html>
|
|
|
|
| 472 |
"<div class='pc-cat'>" + translatedCat + "</div>" +
|
| 473 |
"<div class='pc-desc'>" + desc + "</div>" +
|
| 474 |
"<div class='card-actions'>" +
|
| 475 |
+
"<button class='btn-action btn-ai' onclick=\"openDetail('" + sN + "', '" + sC + "')\">" + t.aiBtn + "</button>" +
|
| 476 |
+
"<button class='btn-action btn-map' onclick=\"window.open('" + mapLink + "', '_blank')\">" + t.mapLinkBtn + "</button></div>";
|
| 477 |
list.appendChild(card);
|
| 478 |
}});
|
| 479 |
}}
|
|
|
|
| 640 |
if(loadingMsg) loadingMsg.remove();
|
| 641 |
|
| 642 |
let finalMsg = res;
|
| 643 |
+
const match = res.match(/\[(.*?)\]/);
|
| 644 |
if(match) {{
|
| 645 |
const pName = match[1];
|
| 646 |
const found = nearbyPlaces.find(p => p.name.includes(pName));
|
|
|
|
| 679 |
|
| 680 |
const popupContent = "<div style='text-align:center'><b>" + p.name + "</b><br>" +
|
| 681 |
"<div style='display:flex; gap:5px; margin-top:5px; justify-content:center;'>" +
|
| 682 |
+
"<button onclick=\"openDetailFromMap('" + sN + "', '" + sC + "')\" style='background:#d4af37; border:none; border-radius:4px; padding:4px 8px; font-size:11px; cursor:pointer;'>๐ค REVIEW</button>" +
|
| 683 |
+
"<button onclick=\"window.open('" + mapUrl + "', '_blank')\" style='background:#333; color:white; border:1px solid #555; border-radius:4px; padding:4px 8px; font-size:11px; cursor:pointer;'>๐บ๏ธ MAP</button>" +
|
| 684 |
"</div></div>";
|
| 685 |
L.marker([p.lat, p.lon]).bindPopup(popupContent).addTo(worldMap);
|
| 686 |
}}
|
|
|
|
| 737 |
const targetLang = TRANSLATIONS[currentLang].ai;
|
| 738 |
const detailMessages = [
|
| 739 |
{{ role: "system", content: "Act as Emily. Write a DETAILED, structured travel report." }},
|
| 740 |
+
{{ role: "user", content: "Write a report for '" + n + "' in '" + c + "'. Include sections: 'Vibe & Atmosphere', 'Must Try Drinks/Food', 'Secret Tip'. Use Markdown. IMPORTANT: Write this report IN " + targetLang + " ONLY. " }}
|
| 741 |
];
|
| 742 |
|
| 743 |
const res = await callGroq(detailMessages);
|