hw_gemini / src /streamlit_app.py
123Sabrina's picture
Update src/streamlit_app.py
28591a6 verified
import pandas as pd
import requests
import google.generativeai as genai
import os
import csv
from datetime import datetime
import streamlit as st
import base64
import io
# Streamlit 配置
st.set_page_config(
page_title="台南開放資料 Gemini 分析",
page_icon="📊",
layout="wide",
initial_sidebar_state="expanded",
)
# 設定 Gemini API 金鑰
GEMINI_API_KEY = st.sidebar.text_input("Gemini API Key", "AIzaSyBxrwdxgs6JemK25piF_RFnxJ9SqKuqhEE", type="password")
if GEMINI_API_KEY:
# 設定 API 金鑰
genai.configure(api_key=GEMINI_API_KEY)
model = genai.GenerativeModel("gemini-1.5-flash")
# 準備 Session State
if 'df' not in st.session_state:
st.session_state.df = None
if 'data_summary' not in st.session_state:
st.session_state.data_summary = ""
if 'chat_history' not in st.session_state:
st.session_state.chat_history = []
if 'csv_log' not in st.session_state:
st.session_state.csv_log = []
# 添加標題行
st.session_state.csv_log.append(["Timestamp", "User", "Gemini"])
# 函數: 記錄對話到 CSV
def log_to_csv(user_message, gemini_response):
timestamp = datetime.now().isoformat()
st.session_state.csv_log.append([timestamp, user_message, gemini_response])
# 函數: 發送訊息到 Gemini
def send_to_gemini(prompt):
if not GEMINI_API_KEY:
return "請先設定 Gemini API Key"
try:
# 組合提示詞,加入上下文
context = ""
if st.session_state.df is not None:
context = f"基於前面提供的台南市資料:\n{st.session_state.data_summary}\n\n"
full_prompt = context + prompt
# 請求 Gemini API
response = model.generate_content(full_prompt)
reply = response.text.strip()
# 記錄到 CSV
log_to_csv(prompt, reply)
return reply
except Exception as e:
return f"⚠️ 錯誤:{str(e)}"
# 函數: 抓取資料
def fetch_data(url):
try:
response = requests.get(url)
response.raise_for_status() # 檢查是否有錯誤
# 儲存為臨時檔案
temp_file = io.BytesIO(response.content)
# 讀取 CSV
df = pd.read_csv(temp_file, encoding="utf-8-sig")
# 更新 session state
st.session_state.df = df
# 準備資料摘要
st.session_state.data_summary = f"""
資料維度: {df.shape[0]} 列 x {df.shape[1]}
欄位名稱: {', '.join(df.columns)}
資料範例:
{df.head(3).to_string()}
"""
return df
except Exception as e:
st.error(f"獲取資料時發生錯誤: {str(e)}")
return None
# 函數: 分析資料
def analyze_data():
if st.session_state.df is None:
st.warning("請先獲取資料")
return
prompt = f"請分析以下台南市政府公開資料,說明這是什麼類型的資料,有什麼重要資訊,以及可能的應用。資料如下:\n\n{st.session_state.data_summary}"
with st.spinner("Gemini 正在分析資料..."):
response = send_to_gemini(prompt)
# 添加到聊天歷史
st.session_state.chat_history.append(("系統", prompt))
st.session_state.chat_history.append(("Gemini", response))
# 自動切換到聊天頁籤
st.session_state.active_tab = "chat"
# 側邊欄
with st.sidebar:
st.title("台南開放資料 Gemini 分析")
st.markdown("---")
# 下載 CSV 按鈕 - 使用原生 download_button 代替 HTML 連結
st.markdown("### 下載對話記錄")
# 準備 CSV 資料
csv_string = io.StringIO()
writer = csv.writer(csv_string)
for row in st.session_state.csv_log:
writer.writerow(row)
csv_bytes = csv_string.getvalue().encode('utf-8-sig')
# 使用原生下載按鈕
st.download_button(
label="下載對話記錄 CSV",
data=csv_bytes,
file_name="chat_log.csv",
mime="text/csv",
)
st.markdown("---")
st.markdown("### 關於")
st.markdown("""
這是一個整合台南市政府開放資料和 Gemini AI 的應用程式。
- 抓取和分析台南市政府的開放資料
- 使用 Google Gemini AI 進行資料解析
- 以 CSV 格式下載分析結果和對話記錄
""")
# 主頁面 - 標籤導航
if 'active_tab' not in st.session_state:
st.session_state.active_tab = "data"
# 標籤選擇器
tabs = ["資料展示", "Gemini 聊天"]
active_tab_index = 0 if st.session_state.active_tab == "data" else 1
active_tab = st.tabs(tabs)[active_tab_index]
# 資料展示頁籤
if active_tab_index == 0:
with active_tab:
st.header("台南市政府開放資料")
# URL 輸入區
default_url = "https://data.tainan.gov.tw/dataset/c4e00530-6367-4b7d-a4ac-085965915c78/resource/fc1990ec-f94f-4845-9cfd-214072d4fbf8/download/eeb5b8ad-a10a-4f6e-a064-f4d6ead73d9d.csv"
url = st.text_input("資料 URL", value=default_url)
col1, col2 = st.columns([1, 5])
with col1:
if st.button("獲取資料"):
with st.spinner("正在獲取資料..."):
fetch_data(url)
with col2:
if st.session_state.df is not None:
if st.button("使用 Gemini 分析資料"):
analyze_data()
st.session_state.active_tab = "chat"
st.rerun() # 修改這裡,從 experimental_rerun() 改為 rerun()
# 顯示資料預覽
if st.session_state.df is not None:
st.subheader("資料預覽")
st.dataframe(st.session_state.df.head(10), use_container_width=True)
# 顯示數據統計
st.subheader("資料統計")
col1, col2 = st.columns(2)
with col1:
st.metric("資料列數", st.session_state.df.shape[0])
with col2:
st.metric("資料欄數", st.session_state.df.shape[1])
# 欄位列表
st.subheader("欄位列表")
st.write(", ".join(st.session_state.df.columns))
# 下載原始資料的按鈕
csv_data = st.session_state.df.to_csv(index=False).encode('utf-8-sig')
st.download_button(
label="下載原始資料 CSV",
data=csv_data,
file_name="tainan_data.csv",
mime="text/csv",
)
# Gemini 聊天頁籤
else:
with active_tab:
st.header("與 Gemini AI 對話")
# 顯示聊天歷史
chat_container = st.container()
with chat_container:
for role, message in st.session_state.chat_history:
if role == "系統":
st.info(f"系統: {message}")
elif role == "使用者":
st.success(f"您: {message}")
else: # Gemini
st.markdown(f"**Gemini**: {message}")
# 使用者輸入區
with st.form(key="chat_form", clear_on_submit=True):
user_input = st.text_area("您的問題:", height=100)
submit_button = st.form_submit_button("送出")
if submit_button and user_input:
# 添加使用者訊息到歷史
st.session_state.chat_history.append(("使用者", user_input))
# 獲取 Gemini 回覆
with st.spinner("Gemini 正在回應..."):
response = send_to_gemini(user_input)
# 添加 Gemini 回覆到歷史
st.session_state.chat_history.append(("Gemini", response))
# 重新加載頁面以顯示新消息
st.rerun() # 修改這裡,從 experimental_rerun() 改為 rerun()