Tashu_Insight / app.py
Adasadqw's picture
Create app.py
3a803c4 verified
import gradio as gr
import pandas as pd
import numpy as np
import joblib
import json
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
import folium
import requests
import os
import firebase_admin
from firebase_admin import credentials, db
from folium.plugins import MarkerCluster
from tensorflow.keras.models import load_model
FIREBASE_WEB_API_KEY = os.environ.get("FIREBASE_WEB_API_KEY")
FIREBASE_KEY_JSON = os.environ.get("FIREBASE_KEY_JSON")
if not FIREBASE_KEY_JSON and os.path.exists("firebase_key.json"):
with open("firebase_key.json") as f:
FIREBASE_KEY_JSON = f.read()
if FIREBASE_KEY_JSON and not firebase_admin._apps:
try:
cred_dict = json.loads(FIREBASE_KEY_JSON)
cred = credentials.Certificate(cred_dict)
firebase_admin.initialize_app(cred, {
'databaseURL': f"https://{cred_dict['project_id']}-default-rtdb.firebaseio.com/"
})
print("โœ… Firebase Admin Connected.")
except Exception as e:
print(f"โŒ Firebase Init Error: {e}")
# ๋ชจ๋ธ ๊ฒฝ๋กœ
MODEL_PATH = 'seq2seq_model3-2.h5'
SCALER_X_PATH = 'scaler_X3-2.save'
SCALER_Y_PATH = 'scaler_y3-2.save'
STATION_DICT_PATH = 'station_dict4-2.json'
DATA_PATH = 'tashu.csv'
TIMESTEPS = 10
FEATURE_COLS = ['temp', 'wind_speed']
DEFAULT_JSON = json.dumps([
{"_time": "2025-11-27 15:00:00", "station_id": "ST0956", "temp": 16.0, "wind_speed": 3.0, "parking_count": 3},
{"_time": "2025-11-27 15:10:00", "station_id": "ST0956", "temp": 16.0, "wind_speed": 3.0, "parking_count": 3},
{"_time": "2025-11-27 15:20:00", "station_id": "ST0956", "temp": 16.0, "wind_speed": 3.0, "parking_count": 2},
{"_time": "2025-11-27 15:30:00", "station_id": "ST0956", "temp": 16.0, "wind_speed": 3.0, "parking_count": 2},
{"_time": "2025-11-27 15:40:00", "station_id": "ST0956", "temp": 16.0, "wind_speed": 3.0, "parking_count": 1},
{"_time": "2025-11-27 15:50:00", "station_id": "ST0956", "temp": 16.0, "wind_speed": 3.0, "parking_count": 0}
], indent=2)
print("Loading System...")
try:
model = load_model(MODEL_PATH, compile=False)
scaler_X = joblib.load(SCALER_X_PATH)
scaler_y = joblib.load(SCALER_Y_PATH)
with open(STATION_DICT_PATH, 'r') as f:
station_dict = json.load(f)
print("โœ… Model Artifacts Loaded.")
except:
model, scaler_X, scaler_y, station_dict = None, None, None, {}
try:
df = pd.read_csv(DATA_PATH)
df['datetime'] = pd.to_datetime(df['_time'])
if df['datetime'].dt.tz is None:
df['datetime'] = df['datetime'].dt.tz_localize('UTC')
df['datetime_kr'] = df['datetime'].dt.tz_convert('Asia/Seoul')
df['hour_kr'] = df['datetime_kr'].dt.hour
df['dayofweek_kr'] = df['datetime_kr'].dt.dayofweek
df['is_weekend'] = df['dayofweek_kr'].apply(lambda x: 'Weekend' if x >= 5 else 'Weekday')
print("โœ… CSV Data Loaded.")
except:
df = pd.DataFrame()
# ------------------------------------------------
# 2. ๋‚ด๋ถ€ ๋กœ์ง
# ------------------------------------------------
def register_user(email, password):
url = f"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key={FIREBASE_WEB_API_KEY}"
payload = {"email": email, "password": password, "returnSecureToken": True}
try:
res = requests.post(url, json=payload)
return "โœ… ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต! ๋กœ๊ทธ์ธํ•ด์ฃผ์„ธ์š”." if res.status_code == 200 else f"โŒ ์˜ค๋ฅ˜: {res.json().get('error', {}).get('message')}"
except Exception as e: return f"โŒ ํ†ต์‹  ์˜ค๋ฅ˜: {e}"
def login_user(email, password):
url = f"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={FIREBASE_WEB_API_KEY}"
payload = {"email": email, "password": password, "returnSecureToken": True}
try:
res = requests.post(url, json=payload)
if res.status_code == 200:
return res.json()['localId'], f"โœ… ๋กœ๊ทธ์ธ ์„ฑ๊ณต! (ID: {email})"
else:
return None, f"โŒ ๋กœ๊ทธ์ธ ์‹คํŒจ: {res.json().get('error', {}).get('message')}"
except Exception as e: return None, f"โŒ ํ†ต์‹  ์˜ค๋ฅ˜: {e}"
def add_favorite(user_id, station_id, alias="ํšŒ์‚ฌ ๊ทผ์ฒ˜"):
if not user_id: return "๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."
try:
station_id = str(station_id).strip()
ref = db.reference(f'users/{user_id}/favorites')
favs = ref.get() or {}
if station_id in favs: return "์ด๋ฏธ ๋“ฑ๋ก๋œ ์ •๋ฅ˜์žฅ์ž…๋‹ˆ๋‹ค."
favs[station_id] = alias
ref.set(favs)
return f"โœ… ์ถ”๊ฐ€ ์™„๋ฃŒ: {station_id}"
except Exception as e: return f"DB ์˜ค๋ฅ˜: {str(e)}"
def delete_favorite(user_id, station_key):
if not user_id: return "๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."
try:
# "ST0956" ์ฒ˜๋Ÿผ ID๋งŒ ๋“ค์–ด์˜จ๋‹ค๊ณ  ๊ฐ€์ •
st_id = station_key.split(' ')[0]
ref = db.reference(f'users/{user_id}/favorites/{st_id}')
ref.delete()
return f"๐Ÿ—‘๏ธ ์‚ญ์ œ ์™„๋ฃŒ: {st_id}"
except Exception as e: return f"์‚ญ์ œ ์˜ค๋ฅ˜: {str(e)}"
def get_favorites(user_id):
if not user_id: return {}
try:
return db.reference(f'users/{user_id}/favorites').get() or {}
except: return {}
def run_prediction_logic(input_df):
if model is None: return np.zeros(6)
input_df['datetime'] = pd.to_datetime(input_df['_time'])
input_df['hour'] = input_df['datetime'].dt.hour
input_df['minute'] = input_df['datetime'].dt.minute
input_df['dayofweek'] = input_df['datetime'].dt.dayofweek
input_df['station_idx'] = input_df['station_id'].map(station_dict).fillna(0)
X_num = input_df[FEATURE_COLS + ['hour','minute','dayofweek']].values.astype(np.float32)
X_scaled = scaler_X.transform(X_num)
station_seq = input_df['station_idx'].values.astype(np.int32)
if len(X_scaled) < TIMESTEPS:
pad_len = TIMESTEPS - len(X_scaled)
X_scaled = np.vstack([np.zeros((pad_len, X_scaled.shape[1]), dtype=np.float32), X_scaled])
station_seq = np.hstack([np.zeros(pad_len, dtype=np.int32), station_seq])
X_scaled = X_scaled.reshape(1, TIMESTEPS, X_scaled.shape[1])
station_seq = station_seq.reshape(1, TIMESTEPS)
y_pred_scaled = model.predict([X_scaled, station_seq], verbose=0)
return scaler_y.inverse_transform(y_pred_scaled.reshape(-1,1)).reshape(-1)
# ------------------------------------------------
# 3. ํƒญ๋ณ„ ์‹œ๊ฐํ™” ํ•จ์ˆ˜
# ------------------------------------------------
def filter_dataframe(station, s_date, e_date, h_start, h_end):
temp_df = df.copy()
try:
s = pd.to_datetime(s_date).tz_localize('Asia/Seoul')
e = pd.to_datetime(e_date).tz_localize('Asia/Seoul') + pd.Timedelta(days=1)
temp_df = temp_df[(temp_df['datetime_kr'] >= s) & (temp_df['datetime_kr'] < e)]
except: pass
temp_df = temp_df[(temp_df['hour_kr'] >= h_start) & (temp_df['hour_kr'] <= h_end)]
if station and station.strip():
temp_df = temp_df[temp_df['station_id'] == station.strip()]
return temp_df
def plot_time_analysis(station, s_date, e_date, h_start, h_end):
target_df = filter_dataframe(station, s_date, e_date, h_start, h_end)
if target_df.empty: return plt.figure()
fig = plt.figure(figsize=(10, 5))
if 'is_weekend' in target_df.columns:
sns.lineplot(data=target_df, x='hour_kr', y='parking_count', hue='is_weekend', errorbar=None)
else:
sns.lineplot(data=target_df, x='hour_kr', y='parking_count', errorbar=None)
plt.title('Hourly Trend')
plt.grid(True, alpha=0.3)
return fig
def plot_temp_analysis(s_date, e_date, h_start, h_end):
target_df = filter_dataframe("", s_date, e_date, h_start, h_end)
if target_df.empty: return plt.figure()
fig = plt.figure(figsize=(10, 5))
target_df['temp_round'] = (target_df['temp'] / 2).round() * 2
temp_agg = target_df.groupby('temp_round')['parking_count'].mean().reset_index()
sns.scatterplot(data=temp_agg, x='temp_round', y='parking_count', s=100, color='red')
sns.lineplot(data=temp_agg, x='temp_round', y='parking_count', color='red', alpha=0.3)
plt.title('Parking Count vs Temperature')
plt.grid(True, alpha=0.3)
return fig
def plot_heatmap(s_date, e_date, h_start, h_end, heatmap_type):
target_df = filter_dataframe("", s_date, e_date, h_start, h_end)
if target_df.empty: return plt.figure()
if heatmap_type == "Weekday": target_df = target_df[target_df['is_weekend'] == 'Weekday']
elif heatmap_type == "Weekend": target_df = target_df[target_df['is_weekend'] == 'Weekend']
fig = plt.figure(figsize=(8, 6))
sns.heatmap(target_df[['parking_count', 'temp', 'wind_speed', 'hour_kr']].corr(), annot=True, cmap='coolwarm', fmt='.2f')
plt.title(f'Correlation ({heatmap_type})')
return fig
def simulate_scenario(station_id, temp, wind, current_count):
now = datetime.datetime.now()
input_list = []
st_id = str(station_id).strip()
for i in range(TIMESTEPS):
past_time = now - datetime.timedelta(minutes=(TIMESTEPS - 1 - i) * 10)
input_list.append({'_time': past_time.strftime('%Y-%m-%d %H:%M:%S'), 'station_id': st_id, 'temp': float(temp), 'wind_speed': float(wind), 'parking_count': float(current_count)})
try:
preds = run_prediction_logic(pd.DataFrame(input_list))
res, x, y = [], [], []
for h, v in enumerate(preds):
val = max(0, float(v))
res.append({"Time": f"+{(h+1)*10}m", "Pred": round(val, 2)})
x.append(f"+{(h+1)*10}m"); y.append(val)
fig = plt.figure(figsize=(10, 4)); plt.plot(x, y, marker='o', color='green'); plt.grid(True, alpha=0.3); plt.ylim(bottom=0)
return pd.DataFrame(res), fig
except: return pd.DataFrame(), None
def draw_map_and_add_fav(temp, wind):
try: meta_df = pd.read_csv("stations_meta.csv")
except: return "ํŒŒ์ผ ์—†์Œ"
m = folium.Map(location=[36.3504, 127.3845], zoom_start=13)
mc = MarkerCluster().add_to(m)
for _, r in meta_df.sample(n=min(100, len(meta_df)), random_state=42).iterrows():
color = 'red' if (15 <= temp <= 25 and wind < 5) else 'green'
folium.Marker([r['lat'], r['lon']], popup=f"{r['name']}<br>{r['station_id']}", icon=folium.Icon(color=color, icon='bicycle', prefix='fa')).add_to(mc)
return m._repr_html_()
def predict_json_manual(j):
try: return pd.DataFrame([{"Step": i, "Pred": float(v)} for i, v in enumerate(run_prediction_logic(pd.DataFrame(json.loads(j))))])
except Exception as e: return pd.DataFrame({"Error": [str(e)]})
# [Tab 5] ๋งˆ์ดํŽ˜์ด์ง€ (UI ๊ฐœ์„  ์ ์šฉ๋จ)
def render_mypage_with_delete_list(user_id, temp, wind):
if not user_id:
return "<h3>๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.</h3>", None, gr.update(choices=[])
favs = get_favorites(user_id)
fav_list = list(favs.keys()) if favs else []
if not favs:
return "<h3>๋“ฑ๋ก๋œ ์ฆ๊ฒจ์ฐพ๊ธฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</h3>", None, gr.update(choices=[])
now = datetime.datetime.now()
plot_data = {}
cards_html = "<div style='display:flex; gap:10px; flex-wrap:wrap;'>"
for st_id, alias in favs.items():
input_list = []
for i in range(TIMESTEPS):
past = now - datetime.timedelta(minutes=(TIMESTEPS - 1 - i) * 10)
input_list.append({'_time': past.strftime('%Y-%m-%d %H:%M:%S'), 'station_id': st_id, 'temp': float(temp), 'wind_speed': float(wind), 'parking_count': 3.0})
preds = run_prediction_logic(pd.DataFrame(input_list))
final_pred = max(0, float(preds[-1]))
# [์š”์ฒญ 2] ๊ทธ๋ž˜ํ”„ ๋ผ๋ฒจ์„ ๋ณ„๋ช… ๋Œ€์‹  ์ •๋ฅ˜์žฅID๋กœ ํ‘œ์‹œ
plot_data[f"{st_id}"] = preds
bg, st_txt = ("#FFCDD2", "๐Ÿ”ด ๋ถ€์กฑ") if final_pred < 1.5 else ("#BBDEFB", "๐Ÿ”ต ๊ณผ์ž‰") if final_pred > 5 else ("#C8E6C9", "๐ŸŸข ์—ฌ์œ ")
# [์š”์ฒญ 1] ์นด๋“œ ๊ธ€์”จ ์ƒ‰์ƒ ๊ฒ€์ •(#000)์œผ๋กœ ๋ณ€๊ฒฝ (๊ฐ€๋…์„ฑ ํ–ฅ์ƒ)
cards_html += f"""
<div style='background-color:{bg}; color:black; padding:15px; border-radius:10px; width:200px; box-shadow:2px 2px 5px #ccc;'>
<h4 style='margin:0;'>{alias}</h4>
<p style='margin:5px 0; font-size:12px; color:#333;'>{st_id}</p>
<h2 style='margin:5px 0; color:black;'>{final_pred:.1f}๋Œ€</h2>
<p style='margin:0; font-weight:bold; color:black;'>{st_txt}</p>
</div>
"""
cards_html += "</div>"
fig = plt.figure(figsize=(10, 5))
for l, y in plot_data.items():
plt.plot([f"+{(i+1)*10}m" for i in range(len(y))], y, marker='o', label=l)
plt.grid(True, alpha=0.3); plt.legend()
plt.title("My Stations Forecast (ID based)")
return cards_html, fig, gr.update(choices=fav_list, value=None)
# ------------------------------------------------
# 4. UI ๊ตฌ์„ฑ
# ------------------------------------------------
theme = gr.themes.Soft(primary_hue="green").set(body_background_fill="*neutral_50")
with gr.Blocks(theme=theme, title="Tashu AI Service") as demo:
user_id_state = gr.State(value=None)
# [Tab 0] ๋กœ๊ทธ์ธ ํƒญ
with gr.Tab("๐Ÿ”‘ ๋กœ๊ทธ์ธ", id="login_tab") as login_tab:
gr.Markdown("# ๐Ÿšฒ ๋Œ€์ „ ํƒ€์Šˆ AI ๊ด€์ œ ์„œ๋น„์Šค์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.")
with gr.Row():
with gr.Column():
l_email = gr.Textbox(label="์ด๋ฉ”์ผ"); l_pass = gr.Textbox(label="๋น„๋ฐ€๋ฒˆํ˜ธ", type="password")
btn_login = gr.Button("๋กœ๊ทธ์ธ", variant="primary")
with gr.Column():
r_email = gr.Textbox(label="์ด๋ฉ”์ผ"); r_pass = gr.Textbox(label="๋น„๋ฐ€๋ฒˆํ˜ธ", type="password")
btn_reg = gr.Button("ํšŒ์›๊ฐ€์ž…")
login_msg = gr.Textbox(label="์•Œ๋ฆผ", interactive=False)
# [Tab 0.5] ํ™ˆ (์ธํŠธ๋กœ)
with gr.Tab("๐Ÿ  ํ™ˆ (์†Œ๊ฐœ)", id="home_tab"):
gr.Markdown(
"""
# ๐Ÿ‘‹ ์•ˆ๋…•ํ•˜์„ธ์š”! ํƒ€์Šˆ AI ๊ด€์ œ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.
๋ณธ ์„œ๋น„์Šค๋Š” **Deep Learning (LSTM)** ๋ชจ๋ธ์„ ํ™œ์šฉํ•˜์—ฌ ๋Œ€์ „ ๊ณต๊ณต์ž์ „๊ฑฐ 'ํƒ€์Šˆ'์˜ ์ˆ˜์š”๋ฅผ ์˜ˆ์ธกํ•˜๊ณ  ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
### ๐Ÿ“Œ ์ฃผ์š” ๊ธฐ๋Šฅ ์•ˆ๋‚ด
* **๐Ÿ“Š ๋ฐ์ดํ„ฐ ์ธ์‚ฌ์ดํŠธ:** ๊ณผ๊ฑฐ 1๊ฐœ์›”์น˜ ๋ฐ์ดํ„ฐ๋ฅผ ์‹œ๊ฐ„, ์˜จ๋„, ์š”์ผ๋ณ„๋กœ ์‹ฌ์ธต ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.
* **๐Ÿ”ฎ ์˜ˆ์ธก ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ:** "์˜ค๋Š˜ ๋‚ ์”จ๊ฐ€ ์ถ”์šฐ๋ฉด ์ž์ „๊ฑฐ๊ฐ€ ๋‚จ์„๊นŒ?" ๊ฐ€์ƒ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋Œ๋ ค๋ณด์„ธ์š”.
* **๐Ÿ—บ๏ธ ์˜ˆ์ธก ์ง€๋„:** ๋Œ€์ „ ์ „์ฒด ์ง€๋„๋ฅผ ๋ณด๋ฉฐ 1์‹œ๊ฐ„ ๋’ค ์ž์ „๊ฑฐ๊ฐ€ ๋ถ€์กฑํ•  ์ง€์—ญ์„ ์‹œ๊ฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
* **๐Ÿ‘ค ๋งˆ์ดํŽ˜์ด์ง€:** ์ž์ฃผ ๊ฐ€๋Š” ์ •๋ฅ˜์žฅ์„ '์ฆ๊ฒจ์ฐพ๊ธฐ'ํ•˜๊ณ , ๋‚˜๋งŒ์˜ ๋งž์ถคํ˜• ์˜ˆ์ธก ๋ฆฌํฌํŠธ๋ฅผ ๋ฐ›์•„๋ณด์„ธ์š”.
---
*์ขŒ์ธก ์ƒ๋‹จ์˜ ํƒญ์„ ํด๋ฆญํ•˜์—ฌ ๊ธฐ๋Šฅ์„ ์ด์šฉํ•ด๋ณด์„ธ์š”!*
"""
)
# [Tab 1] ๋ฐ์ดํ„ฐ ๋ถ„์„
with gr.Tab("๐Ÿ“Š ๋ฐ์ดํ„ฐ ์ธ์‚ฌ์ดํŠธ"):
gr.Markdown("### ๐Ÿ“ˆ ์˜์—ญ๋ณ„ ์ƒ์„ธ ๋ถ„์„")
with gr.Group():
gr.Markdown("#### 1๏ธโƒฃ ์‹œ๊ฐ„๋Œ€๋ณ„ ์ด์šฉ ํŒจํ„ด")
with gr.Row():
t1_st = gr.Textbox(label="์ •๋ฅ˜์žฅ ID (์˜ต์…˜)")
t1_date_s = gr.Textbox(label="์‹œ์ž‘ ๋‚ ์งœ", value="2025-10-20")
t1_date_e = gr.Textbox(label="์ข…๋ฃŒ ๋‚ ์งœ", value="2025-11-25")
with gr.Row():
t1_h_s = gr.Slider(0, 23, 6, step=1, label="์‹œ์ž‘ ์‹œ๊ฐ„")
t1_h_e = gr.Slider(0, 24, 22, step=1, label="์ข…๋ฃŒ ์‹œ๊ฐ„")
btn_plot1 = gr.Button("์‹œ๊ฐ„ ๋ถ„์„ ์‹คํ–‰", variant="primary")
plot1 = gr.Plot()
btn_plot1.click(plot_time_analysis, inputs=[t1_st, t1_date_s, t1_date_e, t1_h_s, t1_h_e], outputs=plot1)
gr.Markdown("---")
with gr.Group():
gr.Markdown("#### 2๏ธโƒฃ ๊ธฐ์˜จ์— ๋”ฐ๋ฅธ ์ด์šฉ๋ฅ ")
with gr.Row():
t2_date_s = gr.Textbox(label="์‹œ์ž‘ ๋‚ ์งœ", value="2025-10-20")
t2_date_e = gr.Textbox(label="์ข…๋ฃŒ ๋‚ ์งœ", value="2025-11-25")
t2_h_s = gr.Slider(0, 23, 6, step=1, label="์‹œ์ž‘ ์‹œ๊ฐ„")
t2_h_e = gr.Slider(0, 24, 22, step=1, label="์ข…๋ฃŒ ์‹œ๊ฐ„")
btn_plot2 = gr.Button("์˜จ๋„ ๋ถ„์„ ์‹คํ–‰", variant="primary")
plot2 = gr.Plot()
btn_plot2.click(plot_temp_analysis, inputs=[t2_date_s, t2_date_e, t2_h_s, t2_h_e], outputs=plot2)
gr.Markdown("---")
with gr.Group():
gr.Markdown("#### 3๏ธโƒฃ ๋ณ€์ˆ˜ ๊ฐ„ ์ƒ๊ด€๊ด€๊ณ„")
with gr.Row():
t3_date_s = gr.Textbox(label="์‹œ์ž‘ ๋‚ ์งœ", value="2025-10-20")
t3_date_e = gr.Textbox(label="์ข…๋ฃŒ ๋‚ ์งœ", value="2025-11-25")
t3_type = gr.Radio(["All", "Weekday", "Weekend"], value="All", label="๋ถ„์„ ๋Œ€์ƒ")
with gr.Row():
t3_h_s = gr.Slider(0, 23, 6, step=1, label="์‹œ์ž‘ ์‹œ๊ฐ„")
t3_h_e = gr.Slider(0, 24, 22, step=1, label="์ข…๋ฃŒ ์‹œ๊ฐ„")
btn_plot3 = gr.Button("์ƒ๊ด€๊ด€๊ณ„ ๋ถ„์„ ์‹คํ–‰", variant="primary")
plot3 = gr.Plot()
btn_plot3.click(plot_heatmap, inputs=[t3_date_s, t3_date_e, t3_h_s, t3_h_e, t3_type], outputs=plot3)
# [Tab 2] ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ
with gr.Tab("๐Ÿ”ฎ ์˜ˆ์ธก ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ"):
with gr.Row():
with gr.Column(scale=1):
inp_st = gr.Textbox(label="์ •๋ฅ˜์žฅ ID", value="ST0003")
inp_temp = gr.Slider(-10, 40, 15, label="๊ธฐ์˜จ")
inp_wind = gr.Slider(0, 30, 2, label="ํ’์†")
inp_cnt = gr.Slider(0, 30, 3, label="ํ˜„์žฌ ๋Œ€์ˆ˜")
btn_sim = gr.Button("์˜ˆ์ธก ์‹คํ–‰", variant="primary")
with gr.Column(scale=2):
out_plot = gr.Plot(); out_df = gr.Dataframe()
btn_sim.click(simulate_scenario, inputs=[inp_st, inp_temp, inp_wind, inp_cnt], outputs=[out_df, out_plot])
# [Tab 3] ์ง€๋„
with gr.Tab("๐Ÿ—บ๏ธ ์ง€๋„ & ์ฆ๊ฒจ์ฐพ๊ธฐ"):
with gr.Row(): m_temp = gr.Slider(-10, 35, 15, label="๊ธฐ์˜จ"); m_wind = gr.Slider(0, 20, 2, label="ํ’์†")
with gr.Row():
with gr.Column(scale=3): out_map = gr.HTML(label="Map")
with gr.Column(scale=1):
fav_id = gr.Textbox(label="์ •๋ฅ˜์žฅ ID"); fav_alias = gr.Textbox(label="๋ณ„๋ช…")
btn_add_fav = gr.Button("์ฆ๊ฒจ์ฐพ๊ธฐ ์ €์žฅ", variant="stop")
fav_msg = gr.Textbox(label="๊ฒฐ๊ณผ")
btn_load_map = gr.Button("์ง€๋„ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ")
btn_load_map.click(draw_map_and_add_fav, inputs=[m_temp, m_wind], outputs=out_map)
btn_add_fav.click(add_favorite, inputs=[user_id_state, fav_id, fav_alias], outputs=fav_msg)
# [Tab 4] JSON
with gr.Tab("๐Ÿ“ JSON ์ž…๋ ฅ"):
inp_json = gr.Code(value=DEFAULT_JSON, language="json")
btn_json = gr.Button("์˜ˆ์ธก"); out_json = gr.Dataframe()
btn_json.click(predict_json_manual, inputs=inp_json, outputs=out_json)
# [Tab 5] ๋งˆ์ดํŽ˜์ด์ง€
with gr.Tab("๐Ÿ‘ค ๋งˆ์ดํŽ˜์ด์ง€"):
gr.Markdown("### ๐ŸŒŸ ๋‚˜์˜ ๋งž์ถคํ˜• ์˜ˆ์ธก ๋ฆฌํฌํŠธ")
btn_refresh = gr.Button("์ƒˆ๋กœ๊ณ ์นจ / ๋ถˆ๋Ÿฌ์˜ค๊ธฐ", variant="primary")
out_cards = gr.HTML()
out_my_plot = gr.Plot()
gr.Markdown("---")
gr.Markdown("#### ๐Ÿ—‘๏ธ ์ฆ๊ฒจ์ฐพ๊ธฐ ๊ด€๋ฆฌ")
with gr.Row():
del_dropdown = gr.Dropdown(label="์‚ญ์ œํ•  ์ •๋ฅ˜์žฅ ์„ ํƒ", choices=[], interactive=True)
btn_delete = gr.Button("์‚ญ์ œํ•˜๊ธฐ", variant="stop")
del_msg = gr.Textbox(label="์‚ญ์ œ ๊ฒฐ๊ณผ", interactive=False)
btn_refresh.click(
render_mypage_with_delete_list,
inputs=[user_id_state, m_temp, m_wind],
outputs=[out_cards, out_my_plot, del_dropdown]
)
def handle_delete(uid, key): return delete_favorite(uid, key)
btn_delete.click(handle_delete, inputs=[user_id_state, del_dropdown], outputs=del_msg)
# ๋กœ๊ทธ์ธ ๋กœ์ง
def handle_login_and_hide(e, p):
uid, msg = login_user(e, p)
return (uid, msg, gr.update(visible=False)) if uid else (None, msg, gr.update(visible=True))
btn_login.click(handle_login_and_hide, inputs=[l_email, l_pass], outputs=[user_id_state, login_msg, login_tab])
btn_reg.click(register_user, inputs=[r_email, r_pass], outputs=login_msg)
if __name__ == "__main__":
demo.launch()