Spaces:
Sleeping
Sleeping
Update apppp.txt
Browse files
apppp.txt
CHANGED
|
@@ -1,21 +1,63 @@
|
|
| 1 |
import gradio as gr
|
|
|
|
|
|
|
| 2 |
import random
|
| 3 |
from datetime import datetime, timedelta, timezone
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
members = ["かほ", "さや", "こず", "るり", "めぐ", "つづ", "ぎん", "すず", "ひめ", "せら", "いず", "さち"]
|
| 7 |
|
| 8 |
def get_personal_daily_oracle(device_id):
|
| 9 |
seed_base = device_id if device_id else "default_fate"
|
| 10 |
jst = timezone(timedelta(hours=9))
|
| 11 |
-
|
|
|
|
| 12 |
|
| 13 |
random.seed(f"{seed_base}_{today_str}")
|
| 14 |
-
|
| 15 |
selected = random.sample(members, 2)
|
| 16 |
random.shuffle(selected)
|
| 17 |
pair_name = f"{selected[0]}{selected[1]}"
|
| 18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
oracle_html = (
|
| 20 |
f"<div id='pair-raw' style='display:none;'>{pair_name}</div>"
|
| 21 |
f"<div style='font-size: 38px; font-weight: normal; margin-bottom: 2px;'>本日の神託</div>"
|
|
@@ -23,10 +65,35 @@ def get_personal_daily_oracle(device_id):
|
|
| 23 |
)
|
| 24 |
peace_msg = "これにより、不毛なカップリング論争は終結しました。"
|
| 25 |
|
| 26 |
-
# 戻り値: oracle_html, peace_msg, bsky_btn_update, x_btn_update, result_box_update
|
| 27 |
return oracle_html, peace_msg, gr.update(visible=True), gr.update(visible=True), gr.update(visible=True)
|
| 28 |
|
| 29 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
js_logic = """
|
| 31 |
function(deviceId) {
|
| 32 |
const id = localStorage.getItem('cp_oracle_device_id') || "guest";
|
|
@@ -44,7 +111,6 @@ function(deviceId) {
|
|
| 44 |
}
|
| 45 |
"""
|
| 46 |
|
| 47 |
-
# JS: 結果の保存
|
| 48 |
js_save_result = """
|
| 49 |
function(oracleHtml, peaceMsg, bskyBtn, xBtn, resultBox) {
|
| 50 |
if (oracleHtml && oracleHtml.includes("本日の神託")) {
|
|
@@ -53,7 +119,6 @@ function(oracleHtml, peaceMsg, bskyBtn, xBtn, resultBox) {
|
|
| 53 |
}
|
| 54 |
"""
|
| 55 |
|
| 56 |
-
# JS: 共通テキスト生成ロジック
|
| 57 |
js_get_share_text = """
|
| 58 |
function() {
|
| 59 |
const pairRawEl = document.getElementById('pair-raw');
|
|
@@ -70,32 +135,26 @@ function() {
|
|
| 70 |
}
|
| 71 |
"""
|
| 72 |
|
| 73 |
-
# JS: 各SNSへの投稿
|
| 74 |
js_share_bluesky = f"function() {{ const text = ({js_get_share_text})(); window.open(`https://bsky.app/intent/compose?text=${{encodeURIComponent(text)}}`, '_blank'); }}"
|
| 75 |
js_share_x = f"function() {{ const text = ({js_get_share_text})(); window.open(`https://twitter.com/intent/tweet?text=${{encodeURIComponent(text)}}`, '_blank'); }}"
|
| 76 |
|
|
|
|
| 77 |
custom_css = """
|
| 78 |
.gradio-container { max-width: 600px !important; text-align: center !important; }
|
| 79 |
-
.center-content { display: flex !important; flex-direction: column !important; align-items: center !important; padding-top:
|
| 80 |
h1 { margin-top: 0px !important; margin-bottom: -5px !important; font-size: 32px !important; }
|
| 81 |
-
#doctrine { font-size: 1.5em !important; line-height: 1.4 !important; font-weight: bold !important; margin-bottom:
|
| 82 |
|
| 83 |
#oracle-box {
|
| 84 |
color: #000 !important; background: #fff !important; border: 4px solid #000 !important;
|
| 85 |
padding: 25px 10px !important; line-height: 1.1 !important; min-height: 130px !important;
|
| 86 |
display: flex !important; flex-direction: column !important; justify-content: center !important;
|
| 87 |
-
margin:
|
| 88 |
}
|
| 89 |
.dark #oracle-box { color: #fff !important; background: #000 !important; border: 4px solid #fff !important; }
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
.action-btn { font-size: 24px !important; font-weight: bold !important; width: 320px !important; border: 2px solid #000 !important; }
|
| 94 |
.dark .action-btn { border: 2px solid #fff !important; }
|
| 95 |
-
|
| 96 |
-
#draw-btn { margin: 0px auto -5px auto !important; height: 75px !important; font-size: 28px !important; }
|
| 97 |
-
#share-bsky { margin: 0px auto -5px auto !important; height: 60px !important; }
|
| 98 |
-
#share-x { margin: 0px auto 10px auto !important; height: 60px !important; }
|
| 99 |
"""
|
| 100 |
|
| 101 |
with gr.Blocks(title="蓮ノ空聖書正典", css=custom_css, theme=gr.themes.Monochrome()) as demo:
|
|
@@ -112,17 +171,29 @@ with gr.Blocks(title="蓮ノ空聖書正典", css=custom_css, theme=gr.themes.Mo
|
|
| 112 |
}
|
| 113 |
""")
|
| 114 |
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
draw_btn.click(
|
| 127 |
fn=get_personal_daily_oracle,
|
| 128 |
inputs=[device_id_storage],
|
|
@@ -138,5 +209,8 @@ with gr.Blocks(title="蓮ノ空聖書正典", css=custom_css, theme=gr.themes.Mo
|
|
| 138 |
share_btn_bsky.click(fn=None, inputs=None, outputs=None, js=js_share_bluesky)
|
| 139 |
share_btn_x.click(fn=None, inputs=None, outputs=None, js=js_share_x)
|
| 140 |
|
|
|
|
|
|
|
|
|
|
| 141 |
if __name__ == "__main__":
|
| 142 |
demo.launch()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import os
|
| 3 |
+
import json
|
| 4 |
import random
|
| 5 |
from datetime import datetime, timedelta, timezone
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from huggingface_hub import CommitScheduler
|
| 8 |
+
|
| 9 |
+
# --- ① 統計データ保存用の設定 ---
|
| 10 |
+
# 自分のリポジトリ名に合わせて変更、または環境変数から取得
|
| 11 |
+
DATASET_REPO_ID = "your-username/hasunosora-stats"
|
| 12 |
+
DATA_FILE = Path("data") / "stats.json"
|
| 13 |
+
DATA_FILE.parent.mkdir(parents=True, exist_ok=True)
|
| 14 |
+
|
| 15 |
+
# 初回起動時にファイルがない場合は空のデータを作成
|
| 16 |
+
if not DATA_FILE.exists():
|
| 17 |
+
with open(DATA_FILE, "w", encoding="utf-8") as f:
|
| 18 |
+
json.dump({"total": 0, "history": []}, f)
|
| 19 |
+
|
| 20 |
+
# Hugging Face Datasetへ自動保存するスケジューラー
|
| 21 |
+
# ※SpaceのSettingsで HF_TOKEN (write権限) の設定が必要です
|
| 22 |
+
scheduler = CommitScheduler(
|
| 23 |
+
repo_id=DATASET_REPO_ID,
|
| 24 |
+
repo_type="dataset",
|
| 25 |
+
folder_path=DATA_FILE.parent,
|
| 26 |
+
path_in_repo="stats.json",
|
| 27 |
+
token=os.getenv("HF_TOKEN")
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
def load_stats():
|
| 31 |
+
if DATA_FILE.exists():
|
| 32 |
+
try:
|
| 33 |
+
with open(DATA_FILE, "r", encoding="utf-8") as f:
|
| 34 |
+
return json.load(f)
|
| 35 |
+
except:
|
| 36 |
+
pass
|
| 37 |
+
return {"total": 0, "history": []}
|
| 38 |
+
|
| 39 |
+
# --- ② 既存ロジックと統計の統合 ---
|
| 40 |
members = ["かほ", "さや", "こず", "るり", "めぐ", "つづ", "ぎん", "すず", "ひめ", "せら", "いず", "さち"]
|
| 41 |
|
| 42 |
def get_personal_daily_oracle(device_id):
|
| 43 |
seed_base = device_id if device_id else "default_fate"
|
| 44 |
jst = timezone(timedelta(hours=9))
|
| 45 |
+
now = datetime.now(jst)
|
| 46 |
+
today_str = now.strftime("%Y-%m-%d")
|
| 47 |
|
| 48 |
random.seed(f"{seed_base}_{today_str}")
|
|
|
|
| 49 |
selected = random.sample(members, 2)
|
| 50 |
random.shuffle(selected)
|
| 51 |
pair_name = f"{selected[0]}{selected[1]}"
|
| 52 |
|
| 53 |
+
# --- ③ 統計の記録処理 ---
|
| 54 |
+
with scheduler.lock:
|
| 55 |
+
stats = load_stats()
|
| 56 |
+
stats["total"] += 1
|
| 57 |
+
stats["history"].append({"date": today_str, "pair": pair_name})
|
| 58 |
+
with open(DATA_FILE, "w", encoding="utf-8") as f:
|
| 59 |
+
json.dump(stats, f, ensure_ascii=False)
|
| 60 |
+
|
| 61 |
oracle_html = (
|
| 62 |
f"<div id='pair-raw' style='display:none;'>{pair_name}</div>"
|
| 63 |
f"<div style='font-size: 38px; font-weight: normal; margin-bottom: 2px;'>本日の神託</div>"
|
|
|
|
| 65 |
)
|
| 66 |
peace_msg = "これにより、不毛なカップリング論争は終結しました。"
|
| 67 |
|
|
|
|
| 68 |
return oracle_html, peace_msg, gr.update(visible=True), gr.update(visible=True), gr.update(visible=True)
|
| 69 |
|
| 70 |
+
# --- 神モード:集計ロジック ---
|
| 71 |
+
def update_stats_view():
|
| 72 |
+
stats = load_stats()
|
| 73 |
+
hist = stats.get("history", [])
|
| 74 |
+
jst = timezone(timedelta(hours=9))
|
| 75 |
+
today_str = datetime.now(jst).strftime("%Y-%m-%d")
|
| 76 |
+
|
| 77 |
+
counts_all = {}
|
| 78 |
+
counts_today = {}
|
| 79 |
+
|
| 80 |
+
for h in hist:
|
| 81 |
+
p = h['pair']
|
| 82 |
+
counts_all[p] = counts_all.get(p, 0) + 1
|
| 83 |
+
if h.get('date') == today_str:
|
| 84 |
+
counts_today[p] = counts_today.get(p, 0) + 1
|
| 85 |
+
|
| 86 |
+
def gen_rank_html(title, data_dict):
|
| 87 |
+
sorted_items = sorted(data_dict.items(), key=lambda x: x[1], reverse=True)[:5]
|
| 88 |
+
rows = "".join([f"<li>{i+1}位: <b>{p}</b> ({c}回)</li>" for i, (p, c) in enumerate(sorted_items)])
|
| 89 |
+
return f"<h4>{title}</h4><ul>{rows if rows else '<li>データなし</li>'}</ul>"
|
| 90 |
+
|
| 91 |
+
html_all = gen_rank_html("全人類神託カップリングTOP5【累計】", counts_all)
|
| 92 |
+
html_today = gen_rank_html("全人類神託カップリングTOP5【本日】", counts_today)
|
| 93 |
+
|
| 94 |
+
return f"<div style='text-align: left;'>{html_all}<hr>{html_today}<p>総神託数: {stats['total']}</p></div>"
|
| 95 |
+
|
| 96 |
+
# --- JS: 既存のものを維持 ---
|
| 97 |
js_logic = """
|
| 98 |
function(deviceId) {
|
| 99 |
const id = localStorage.getItem('cp_oracle_device_id') || "guest";
|
|
|
|
| 111 |
}
|
| 112 |
"""
|
| 113 |
|
|
|
|
| 114 |
js_save_result = """
|
| 115 |
function(oracleHtml, peaceMsg, bskyBtn, xBtn, resultBox) {
|
| 116 |
if (oracleHtml && oracleHtml.includes("本日の神託")) {
|
|
|
|
| 119 |
}
|
| 120 |
"""
|
| 121 |
|
|
|
|
| 122 |
js_get_share_text = """
|
| 123 |
function() {
|
| 124 |
const pairRawEl = document.getElementById('pair-raw');
|
|
|
|
| 135 |
}
|
| 136 |
"""
|
| 137 |
|
|
|
|
| 138 |
js_share_bluesky = f"function() {{ const text = ({js_get_share_text})(); window.open(`https://bsky.app/intent/compose?text=${{encodeURIComponent(text)}}`, '_blank'); }}"
|
| 139 |
js_share_x = f"function() {{ const text = ({js_get_share_text})(); window.open(`https://twitter.com/intent/tweet?text=${{encodeURIComponent(text)}}`, '_blank'); }}"
|
| 140 |
|
| 141 |
+
# --- CSS ---
|
| 142 |
custom_css = """
|
| 143 |
.gradio-container { max-width: 600px !important; text-align: center !important; }
|
| 144 |
+
.center-content { display: flex !important; flex-direction: column !important; align-items: center !important; padding-top: 20px !important; }
|
| 145 |
h1 { margin-top: 0px !important; margin-bottom: -5px !important; font-size: 32px !important; }
|
| 146 |
+
#doctrine { font-size: 1.5em !important; line-height: 1.4 !important; font-weight: bold !important; margin-bottom: 10px !important; text-align: center !important; }
|
| 147 |
|
| 148 |
#oracle-box {
|
| 149 |
color: #000 !important; background: #fff !important; border: 4px solid #000 !important;
|
| 150 |
padding: 25px 10px !important; line-height: 1.1 !important; min-height: 130px !important;
|
| 151 |
display: flex !important; flex-direction: column !important; justify-content: center !important;
|
| 152 |
+
margin: 10px auto !important;
|
| 153 |
}
|
| 154 |
.dark #oracle-box { color: #fff !important; background: #000 !important; border: 4px solid #fff !important; }
|
| 155 |
+
#peace-msg { font-size: 20px !important; font-weight: bold !important; color: #d63031 !important; margin-top: 10px !important; }
|
| 156 |
+
.action-btn { font-size: 24px !important; font-weight: bold !important; width: 320px !important; border: 2px solid #000 !important; margin-bottom: 10px !important; }
|
|
|
|
|
|
|
| 157 |
.dark .action-btn { border: 2px solid #fff !important; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
"""
|
| 159 |
|
| 160 |
with gr.Blocks(title="蓮ノ空聖書正典", css=custom_css, theme=gr.themes.Monochrome()) as demo:
|
|
|
|
| 171 |
}
|
| 172 |
""")
|
| 173 |
|
| 174 |
+
gr.Markdown("# ⚖️ 蓮ノ空聖書正典")
|
| 175 |
+
|
| 176 |
+
with gr.Tabs():
|
| 177 |
+
# --- ① 信仰(メイン)タブ ---
|
| 178 |
+
with gr.TabItem("神託"):
|
| 179 |
+
with gr.Column(elem_classes="center-content"):
|
| 180 |
+
gr.Markdown("日付が変わるまであなたの思想は<br>統一されます。", elem_id="doctrine")
|
| 181 |
+
|
| 182 |
+
result_display = gr.HTML(elem_id="oracle-box", visible=False)
|
| 183 |
+
peace_display = gr.Markdown(elem_id="peace-msg")
|
| 184 |
+
|
| 185 |
+
draw_btn = gr.Button("神託を受ける", variant="primary", elem_id="draw-btn", elem_classes="action-btn")
|
| 186 |
+
share_btn_bsky = gr.Button("Blueskyで信仰を広める", variant="secondary", elem_id="share-bsky", elem_classes="action-btn", visible=False)
|
| 187 |
+
share_btn_x = gr.Button("Xで信仰を広める", variant="secondary", elem_id="share-x", elem_classes="action-btn", visible=False)
|
| 188 |
+
|
| 189 |
+
# --- ② 神モードタブ ---
|
| 190 |
+
with gr.TabItem("神モード"):
|
| 191 |
+
with gr.Column(elem_classes="center-content"):
|
| 192 |
+
gr.Markdown("### 全人類の信仰状況")
|
| 193 |
+
stats_display = gr.HTML("<p>ボタンを押して読み込み</p>")
|
| 194 |
+
refresh_stats = gr.Button("全人類の神託を見る", variant="primary", elem_classes="action-btn")
|
| 195 |
+
|
| 196 |
+
# --- 既存のクリックイベント ---
|
| 197 |
draw_btn.click(
|
| 198 |
fn=get_personal_daily_oracle,
|
| 199 |
inputs=[device_id_storage],
|
|
|
|
| 209 |
share_btn_bsky.click(fn=None, inputs=None, outputs=None, js=js_share_bluesky)
|
| 210 |
share_btn_x.click(fn=None, inputs=None, outputs=None, js=js_share_x)
|
| 211 |
|
| 212 |
+
# --- 新機能のクリックイベント ---
|
| 213 |
+
refresh_stats.click(fn=update_stats_view, outputs=stats_display)
|
| 214 |
+
|
| 215 |
if __name__ == "__main__":
|
| 216 |
demo.launch()
|