Nagomi-SugarHeart commited on
Commit
2a5da32
·
verified ·
1 Parent(s): 040ecad

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -56
app.py CHANGED
@@ -1,8 +1,10 @@
 
1
  import streamlit as st
2
  import google.generativeai as genai
3
  import requests
4
  import os
5
- import re # HTMLタグ除去用のライブラリ
 
6
 
7
  # --- 初期設定 ---
8
  GEMINI_KEY = os.environ.get("GEMINI_API_KEY")
@@ -13,19 +15,25 @@ AUTHOR_LIST = ["にき", "かん", "なごみ", "蜜柑餅"]
13
  if GEMINI_KEY:
14
  genai.configure(api_key=GEMINI_KEY)
15
 
16
- # --- デバッグ用コード(将来のモデル確認用) ---
17
- # 画面に常に表示されると邪魔な場合は、サイドバーに隠しておくこともできます
18
- with st.sidebar.expander("🔍 デバッグ・モデル確認モード"):
19
- if not GEMINI_KEY:
20
- st.error("❌ GEMINI_API_KEY 未設定")
21
- else:
22
- st.success("✅ APIキー読み込み済み")
23
- try:
24
- available_models = [m.name for m in genai.list_models() if 'generateContent' in m.supported_generation_methods]
25
- st.write("利用可能なモデル一覧:")
26
- st.write(available_models)
27
- except Exception as e:
28
- st.error(f"取得エラー: {e}")
 
 
 
 
 
 
29
 
30
  # --- 関数:microCMSから記事を取得 ---
31
  def get_articles():
@@ -43,8 +51,8 @@ def get_articles():
43
  # --- 画面制御 ---
44
  params = st.query_params
45
 
46
- # 1. 個別記事ページ(URLに ?id=xxx がある場合)
47
  if "id" in params:
 
48
  article_id = params["id"]
49
  articles = get_articles()
50
  article = next((a for a in articles if a["id"] == article_id), None)
@@ -55,16 +63,14 @@ if "id" in params:
55
  st.rerun()
56
 
57
  st.title(article.get("title", "無題"))
58
- st.caption(f"著者: {article.get('author')} | 公開日: {article.get('publishedAt')[:10]}")
59
  st.divider()
60
- # 本文をHTMLとして表示
61
  st.markdown(article.get("content", ""), unsafe_allow_html=True)
62
  st.divider()
63
 
64
- # SNS共有用URL
65
- space_id = os.environ.get('SPACE_ID', 'your-space-id')
66
  share_url = f"https://huggingface.co/spaces/{space_id}?id={article_id}"
67
- st.write("🔗 この記事の固定リンク:")
68
  st.code(share_url)
69
  else:
70
  st.error("記事が見つかりませんでした。")
@@ -72,71 +78,69 @@ if "id" in params:
72
  st.query_params.clear()
73
  st.rerun()
74
 
75
- # 2. メイン画面(一覧表示 & 投稿)
76
  else:
 
77
  tab_view, tab_post = st.tabs(["📖 ブログを読む", "✍️ 記事を書く"])
78
 
79
  with tab_view:
80
  st.title("サークル活動ブログ")
81
-
82
- # 検索・絞り込み機能
83
  search_col1, search_col2 = st.columns(2)
84
  with search_col1:
85
- search_query = st.text_input("🔍 タイトル・本文で検索", "")
86
  with search_col2:
87
- # 検索用リストには「全員」を追加
88
  search_author_list = ["全員"] + AUTHOR_LIST
89
  author_filter = st.selectbox("👤 著者で絞り込み", search_author_list)
90
 
91
- articles = get_articles()
92
-
93
- # 一覧表示
94
  for a in articles:
95
  title = a.get("title", "無題")
96
  content_html = a.get("content", "")
97
  author = a.get("author", "不明")
98
  pub_date = a.get("publishedAt", "-----")[:10]
99
-
100
  clean_text = re.sub(r'<[^>]*?>', '', content_html)
101
 
102
- # フィルタリング判定
103
- author_match = (author_filter == "全員") or (author_filter == author)
104
- search_match = (search_query.lower() in title.lower() or search_query.lower() in clean_text.lower())
105
-
106
- if author_match and search_match:
107
  with st.container():
108
  st.subheader(title)
109
- st.caption(f"著者: {author} | 公開日: {pub_date}")
110
  snippet = clean_text[:100] + "..."
111
  st.write(f'<p style="font-size:0.9em; color:#555;">{snippet}</p>', unsafe_allow_html=True)
112
-
113
  if st.button("続きを読む", key=a["id"]):
114
  st.query_params.id = a["id"]
115
  st.rerun()
116
  st.divider()
117
 
118
- # 記事投稿画面(tab_post のスコープ内にすべて収める)
119
  with tab_post:
120
  st.title("秘密の管理画面")
121
-
122
- password = st.text_input("合言葉を入力してください", type="password")
123
 
124
  if password == "1234":
125
  st.success("ログイン成功!")
126
 
127
- title_input = st.text_input("タイトル")
128
- author_select = st.selectbox("著者を選択", AUTHOR_LIST)
129
- raw_content = st.text_area("本文を入力 (メモ書きでOK)", height=150)
 
 
 
 
 
130
 
131
- if st.button("✨ AIモードでブログ風に装飾する"):
132
  if raw_content:
133
  try:
134
- # 解決済みの最新モデル指定
135
- model = genai.GenerativeModel(model_name='gemini-2.5-flash')
136
- prompt = f"以下の文章をブログ記事用に、読みやすいHTMLタグ(h3, p, strong, br等)を使って装飾してください。なお、出力結果には余計な回答(```html等)は含めず純粋なhtmlコードのみを必ず出力すること。本文: {raw_content}"
137
- with st.spinner('Geminiが執筆中...'):
 
 
 
 
 
 
138
  ai_response = model.generate_content(prompt)
139
- # 万が一 ``` が含まれた場合の除去
140
  clean_html = ai_response.text.replace('```html', '').replace('```', '').strip()
141
  st.session_state.formatted_html = clean_html
142
  except Exception as e:
@@ -144,21 +148,48 @@ else:
144
  else:
145
  st.warning("本文を入力してください")
146
 
 
147
  if "formatted_html" in st.session_state:
148
- st.write("▼ AIが作成したHTML(微調整可)")
149
- final_html = st.text_area("HTML内容", value=st.session_state.formatted_html, height=200)
150
 
151
- if st.button("🚀 ブログを公開する"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  url = f"https://{CMS_SERVICE_ID}.microcms.io/api/v1/blog"
153
  headers = {"X-MICROCMS-API-KEY": CMS_API_KEY, "Content-Type": "application/json"}
154
- post_data = {"title": title_input, "content": final_html, "author": author_select}
 
 
 
 
 
 
 
 
155
 
156
  res = requests.post(url, headers=headers, json=post_data)
157
  if res.status_code == 201:
158
- st.success("投稿完了!「ブログを読む」タブを確認してください。")
159
- if "formatted_html" in st.session_state:
160
- del st.session_state.formatted_html
161
  else:
162
- st.error(f"投稿失敗:{res.text}")
163
  elif password != "":
164
  st.error("合言葉が違います。")
 
 
1
+ ```python
2
  import streamlit as st
3
  import google.generativeai as genai
4
  import requests
5
  import os
6
+ import re
7
+ from datetime import datetime
8
 
9
  # --- 初期設定 ---
10
  GEMINI_KEY = os.environ.get("GEMINI_API_KEY")
 
15
  if GEMINI_KEY:
16
  genai.configure(api_key=GEMINI_KEY)
17
 
18
+ # --- サイドバー:デバッグ & モデル設定 ---
19
+ with st.sidebar:
20
+ st.title("⚙️ 設定")
21
+ model_choice = st.selectbox(
22
+ "使用するAIモデル",
23
+ ["gemini-1.5-flash", "gemini-1.5-pro", "gemini-2.0-flash-exp"],
24
+ index=0,
25
+ help="Proは賢いですが少し遅いです。Flashは爆速です。"
26
+ )
27
+
28
+ with st.expander("🔍 デバッグ情報"):
29
+ if not GEMINI_KEY:
30
+ st.error("❌ APIキー未設定")
31
+ else:
32
+ try:
33
+ available_models = [m.name for m in genai.list_models() if 'generateContent' in m.supported_generation_methods]
34
+ st.write("利用可能モデル:", available_models)
35
+ except Exception as e:
36
+ st.error(f"取得失敗: {e}")
37
 
38
  # --- 関数:microCMSから記事を取得 ---
39
  def get_articles():
 
51
  # --- 画面制御 ---
52
  params = st.query_params
53
 
 
54
  if "id" in params:
55
+ # --- 個別記事ページ ---
56
  article_id = params["id"]
57
  articles = get_articles()
58
  article = next((a for a in articles if a["id"] == article_id), None)
 
63
  st.rerun()
64
 
65
  st.title(article.get("title", "無題"))
66
+ st.caption(f"👤 {article.get('author')} | 📅 {article.get('publishedAt')[:10]}")
67
  st.divider()
 
68
  st.markdown(article.get("content", ""), unsafe_allow_html=True)
69
  st.divider()
70
 
71
+ space_id = os.environ.get('SPACE_ID', 'default')
 
72
  share_url = f"https://huggingface.co/spaces/{space_id}?id={article_id}"
73
+ st.write("🔗 固定リンク:")
74
  st.code(share_url)
75
  else:
76
  st.error("記事が見つかりませんでした。")
 
78
  st.query_params.clear()
79
  st.rerun()
80
 
 
81
  else:
82
+ # --- メイン画面 ---
83
  tab_view, tab_post = st.tabs(["📖 ブログを読む", "✍️ 記事を書く"])
84
 
85
  with tab_view:
86
  st.title("サークル活動ブログ")
 
 
87
  search_col1, search_col2 = st.columns(2)
88
  with search_col1:
89
+ search_query = st.text_input("🔍 本文・タイトル検索", "")
90
  with search_col2:
 
91
  search_author_list = ["全員"] + AUTHOR_LIST
92
  author_filter = st.selectbox("👤 著者で絞り込み", search_author_list)
93
 
94
+ articles = get_articles()
 
 
95
  for a in articles:
96
  title = a.get("title", "無題")
97
  content_html = a.get("content", "")
98
  author = a.get("author", "不明")
99
  pub_date = a.get("publishedAt", "-----")[:10]
 
100
  clean_text = re.sub(r'<[^>]*?>', '', content_html)
101
 
102
+ if ((author_filter == "全員") or (author_filter == author)) and \
103
+ (search_query.lower() in title.lower() or search_query.lower() in clean_text.lower()):
 
 
 
104
  with st.container():
105
  st.subheader(title)
106
+ st.caption(f"👤 {author} | 📅 {pub_date}")
107
  snippet = clean_text[:100] + "..."
108
  st.write(f'<p style="font-size:0.9em; color:#555;">{snippet}</p>', unsafe_allow_html=True)
 
109
  if st.button("続きを読む", key=a["id"]):
110
  st.query_params.id = a["id"]
111
  st.rerun()
112
  st.divider()
113
 
 
114
  with tab_post:
115
  st.title("秘密の管理画面")
116
+ password = st.text_input("合言葉", type="password")
 
117
 
118
  if password == "1234":
119
  st.success("ログイン成功!")
120
 
121
+ # 入力エリア
122
+ col_t, col_a = st.columns([2, 1])
123
+ with col_t:
124
+ title_input = st.text_input("記事タイトル")
125
+ with col_a:
126
+ author_select = st.selectbox("著者", AUTHOR_LIST)
127
+
128
+ raw_content = st.text_area("本文メモ(AIが装飾します)", height=150)
129
 
130
+ if st.button("✨ AIモードで装飾・プレビュー生成"):
131
  if raw_content:
132
  try:
133
+ model = genai.GenerativeModel(model_name=model_choice)
134
+ prompt = f"""
135
+ 以下の文章をブログ記事用に、読みやすいHTMLタグ(h3, p, strong, br等)を使って装飾してください。
136
+ 【ルール】
137
+ - 返答は純粋なHTMLコードのみ。
138
+ - 挨拶や解説、```html などの囲みは一切不要。
139
+ - オシャレでモダンな作りかつ、読者がワクワクするような構成。
140
+ 本文: {raw_content}
141
+ """
142
+ with st.spinner(f'{model_choice} が執筆中...'):
143
  ai_response = model.generate_content(prompt)
 
144
  clean_html = ai_response.text.replace('```html', '').replace('```', '').strip()
145
  st.session_state.formatted_html = clean_html
146
  except Exception as e:
 
148
  else:
149
  st.warning("本文を入力してください")
150
 
151
+ # プレビュー & 最終編集エリア
152
  if "formatted_html" in st.session_state:
153
+ st.divider()
154
+ st.subheader("👀 プレビュー・最終確認")
155
 
156
+ # プレビュー表示
157
+ with st.container():
158
+ st.info("実際の表示イメージ:")
159
+ st.markdown(f"""
160
+ <div style="border:1px solid #ddd; padding:20px; border-radius:10px; background:#fff; color:#333;">
161
+ <h3>{title_input}</h3>
162
+ <small>著者: {author_select}</small>
163
+ <hr>
164
+ {st.session_state.formatted_html}
165
+ </div>
166
+ """, unsafe_allow_html=True)
167
+
168
+ st.write("▼ HTMLを微調整(必要であれば)")
169
+ final_html = st.text_area("編集用コード", value=st.session_state.formatted_html, height=150)
170
+
171
+ # 公開設定
172
+ status = st.radio("公開設定", ["すぐに公開", "下書きとして保存"], horizontal=True)
173
+
174
+ if st.button("🚀 microCMSへ送信"):
175
  url = f"https://{CMS_SERVICE_ID}.microcms.io/api/v1/blog"
176
  headers = {"X-MICROCMS-API-KEY": CMS_API_KEY, "Content-Type": "application/json"}
177
+
178
+
179
+ display_title = title_input if status == "すぐに公開" else f"【下書き】{title_input}"
180
+
181
+ post_data = {
182
+ "title": display_title,
183
+ "content": final_html,
184
+ "author": author_select
185
+ }
186
 
187
  res = requests.post(url, headers=headers, json=post_data)
188
  if res.status_code == 201:
189
+ st.success(f"送信完了!ステータス: {status}")
190
+ del st.session_state.formatted_html
 
191
  else:
192
+ st.error(f"送信失敗:{res.text}")
193
  elif password != "":
194
  st.error("合言葉が違います。")
195
+