feat: migrate stats display to dynamic Shields.io badges via Gist
This commit is contained in:
@@ -632,8 +632,75 @@ class OpenWebUIStats:
|
|||||||
json.dump(data, f, indent=2)
|
json.dump(data, f, indent=2)
|
||||||
print(f" 📊 Generated badge: {name}.json")
|
print(f" 📊 Generated badge: {name}.json")
|
||||||
|
|
||||||
|
if self.gist_token and self.gist_id:
|
||||||
|
try:
|
||||||
|
# 构造并上传 Shields.io 徽章数据
|
||||||
|
self.upload_gist_badges(stats)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ 徽章生成失败: {e}")
|
||||||
|
|
||||||
print(f"✅ Shields.io endpoints saved to: {output_dir}/")
|
print(f"✅ Shields.io endpoints saved to: {output_dir}/")
|
||||||
|
|
||||||
|
def upload_gist_badges(self, stats: dict):
|
||||||
|
"""生成并上传 Gist 徽章数据 (用于 Shields.io Endpoint)"""
|
||||||
|
if not (self.gist_token and self.gist_id):
|
||||||
|
return
|
||||||
|
|
||||||
|
delta = self.get_stat_delta(stats)
|
||||||
|
|
||||||
|
# 定义徽章配置 {key: (label, value, color)}
|
||||||
|
badges_config = {
|
||||||
|
"downloads": ("Downloads", stats["total_downloads"], "brightgreen"),
|
||||||
|
"views": ("Views", stats["total_views"], "blue"),
|
||||||
|
"upvotes": ("Upvotes", stats["total_upvotes"], "orange"),
|
||||||
|
"followers": (
|
||||||
|
"Followers",
|
||||||
|
stats.get("user", {}).get("followers", 0),
|
||||||
|
"blueviolet",
|
||||||
|
),
|
||||||
|
"points": (
|
||||||
|
"Points",
|
||||||
|
stats.get("user", {}).get("total_points", 0),
|
||||||
|
"yellow",
|
||||||
|
),
|
||||||
|
"posts": ("Posts", stats["total_posts"], "informational"),
|
||||||
|
}
|
||||||
|
|
||||||
|
files_payload = {}
|
||||||
|
for key, (label, val, color) in badges_config.items():
|
||||||
|
diff = delta.get(key, 0)
|
||||||
|
|
||||||
|
message = f"{val}"
|
||||||
|
if diff > 0:
|
||||||
|
message += f" (+{diff}🚀)"
|
||||||
|
elif diff < 0:
|
||||||
|
message += f" ({diff})"
|
||||||
|
|
||||||
|
# 构造 Shields.io endpoint JSON
|
||||||
|
# 参考: https://shields.io/badges/endpoint-badge
|
||||||
|
badge_data = {
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"label": label,
|
||||||
|
"message": message,
|
||||||
|
"color": color,
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = f"badge_{key}.json"
|
||||||
|
files_payload[filename] = {
|
||||||
|
"content": json.dumps(badge_data, ensure_ascii=False)
|
||||||
|
}
|
||||||
|
|
||||||
|
# 批量上传到 Gist
|
||||||
|
url = f"https://api.github.com/gists/{self.gist_id}"
|
||||||
|
headers = {"Authorization": f"token {self.gist_token}"}
|
||||||
|
payload = {"files": files_payload}
|
||||||
|
|
||||||
|
resp = requests.patch(url, headers=headers, json=payload)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
print(f"✅ 动态徽章已同步至 Gist ({len(files_payload)} files)")
|
||||||
|
else:
|
||||||
|
print(f"⚠️ 徽章上传失败: {resp.status_code} {resp.text}")
|
||||||
|
|
||||||
def generate_readme_stats(self, stats: dict, lang: str = "zh") -> str:
|
def generate_readme_stats(self, stats: dict, lang: str = "zh") -> str:
|
||||||
"""
|
"""
|
||||||
生成 README 统计区域 (精简版)
|
生成 README 统计区域 (精简版)
|
||||||
@@ -683,6 +750,32 @@ class OpenWebUIStats:
|
|||||||
lines.append(f"> {t['updated']}")
|
lines.append(f"> {t['updated']}")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
|
# 定义徽章 URL (使用 Gist ID)
|
||||||
|
# 注意: 这里使用 shields.io 的 endpoint 功能,直接读取 Gist Raw URL
|
||||||
|
# URL 格式: https://gist.githubusercontent.com/{gist_user}/{gist_id}/raw/badge_{key}.json
|
||||||
|
# 由于我们不知道 gist_user,但 Gist ID 是全局唯一的,我们可以用 shields.io 的兼容性或者假设用户名为 Fu-Jie (根据 user request)
|
||||||
|
# 为了通用性,我们这里使用 requests 获取一次 final URL 或者直接硬编码 Fu-Jie (因为 Gist 是私有的或者为了简单)
|
||||||
|
# 用户明确提到: https://gist.github.com/Fu-Jie/db3d95687075a880af6f1fba76d679c6
|
||||||
|
gist_user = "Fu-Jie"
|
||||||
|
if self.gist_id:
|
||||||
|
base_badge_url = f"https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/{gist_user}/{self.gist_id}/raw"
|
||||||
|
else:
|
||||||
|
base_badge_url = ""
|
||||||
|
|
||||||
|
def get_badge(key: str, style: str = "flat") -> str:
|
||||||
|
if not base_badge_url:
|
||||||
|
# 降级:如果没有 Gist,显示静态文本
|
||||||
|
val = stats.get(f"total_{key}", 0)
|
||||||
|
if key == "followers":
|
||||||
|
val = user.get("followers", 0)
|
||||||
|
if key == "points":
|
||||||
|
val = user.get("total_points", 0)
|
||||||
|
if key == "posts":
|
||||||
|
val = stats.get("total_posts", 0)
|
||||||
|
return f"**{val}**{fmt_delta(key)}"
|
||||||
|
|
||||||
|
return f""
|
||||||
|
|
||||||
# 作者信息表格
|
# 作者信息表格
|
||||||
if user:
|
if user:
|
||||||
username = user.get("username", "")
|
username = user.get("username", "")
|
||||||
@@ -690,8 +783,8 @@ class OpenWebUIStats:
|
|||||||
lines.append(t["author_header"])
|
lines.append(t["author_header"])
|
||||||
lines.append("| :---: | :---: | :---: | :---: |")
|
lines.append("| :---: | :---: | :---: | :---: |")
|
||||||
lines.append(
|
lines.append(
|
||||||
f"| [{username}]({profile_url}) | **{user.get('followers', 0)}**{fmt_delta('followers')} | "
|
f"| [{username}]({profile_url}) | {get_badge('followers')} | "
|
||||||
f"**{user.get('total_points', 0)}**{fmt_delta('points')} | **{user.get('contributions', 0)}** |"
|
f"{get_badge('points')} | **{user.get('contributions', 0)}** |"
|
||||||
)
|
)
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
@@ -699,8 +792,8 @@ class OpenWebUIStats:
|
|||||||
lines.append(t["header"])
|
lines.append(t["header"])
|
||||||
lines.append("| :---: | :---: | :---: | :---: | :---: |")
|
lines.append("| :---: | :---: | :---: | :---: | :---: |")
|
||||||
lines.append(
|
lines.append(
|
||||||
f"| **{stats['total_posts']}** | **{stats['total_downloads']}**{fmt_delta('downloads')} | "
|
f"| {get_badge('posts')} | {get_badge('downloads')} | "
|
||||||
f"**{stats['total_views']}**{fmt_delta('views')} | **{stats['total_upvotes']}**{fmt_delta('upvotes')} | **{stats['total_saves']}** |"
|
f"{get_badge('views')} | {get_badge('upvotes')} | **{stats['total_saves']}** |"
|
||||||
)
|
)
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user