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()