Spaces:
Sleeping
Sleeping
Commit
·
2bfaee6
1
Parent(s):
cfcca52
update
Browse files- app.py +47 -3
- templates/dashboard.html +13 -3
app.py
CHANGED
|
@@ -4,6 +4,7 @@ import urllib.parse
|
|
| 4 |
import sqlite3
|
| 5 |
import json
|
| 6 |
import time
|
|
|
|
| 7 |
from flask import Flask, redirect, request, session, render_template, url_for, g
|
| 8 |
import requests
|
| 9 |
|
|
@@ -25,6 +26,39 @@ CACHE_TTL = 300 # 5 minutes
|
|
| 25 |
DB_PATH = "cache.db"
|
| 26 |
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
def get_db():
|
| 29 |
if "db" not in g:
|
| 30 |
g.db = sqlite3.connect(DB_PATH)
|
|
@@ -126,7 +160,7 @@ def fetch_space_discussions(space_id, token=None):
|
|
| 126 |
return discussions
|
| 127 |
|
| 128 |
|
| 129 |
-
def get_discussions_feed(username, token=None):
|
| 130 |
spaces = fetch_user_spaces(username, token)
|
| 131 |
|
| 132 |
all_discussions = []
|
|
@@ -136,9 +170,17 @@ def get_discussions_feed(username, token=None):
|
|
| 136 |
|
| 137 |
for d in discussions:
|
| 138 |
owner_responded = d.get("repoOwner", {}).get("isParticipating", False)
|
|
|
|
|
|
|
|
|
|
| 139 |
base_score = d.get("numComments", 0) + d.get("numReactionUsers", 0) * 2
|
|
|
|
| 140 |
# Demote if owner has responded
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
all_discussions.append({
|
| 143 |
"space_id": space_id,
|
| 144 |
"space_name": space_id.split("/")[-1] if "/" in space_id else space_id,
|
|
@@ -149,11 +191,13 @@ def get_discussions_feed(username, token=None):
|
|
| 149 |
"author": d.get("author", {}).get("name", "unknown"),
|
| 150 |
"author_avatar": d.get("author", {}).get("avatarUrl", ""),
|
| 151 |
"created_at": d.get("createdAt", ""),
|
|
|
|
| 152 |
"num_comments": d.get("numComments", 0),
|
| 153 |
"num_reactions": d.get("numReactionUsers", 0),
|
| 154 |
"top_reactions": d.get("topReactions", []),
|
| 155 |
"score": score,
|
| 156 |
"owner_responded": owner_responded,
|
|
|
|
| 157 |
"url": f"https://huggingface.co/spaces/{space_id}/discussions/{d.get('num')}",
|
| 158 |
})
|
| 159 |
|
|
@@ -260,7 +304,7 @@ def dashboard():
|
|
| 260 |
filter_status = request.args.get("status", "open")
|
| 261 |
|
| 262 |
spaces = fetch_user_spaces(username, token)
|
| 263 |
-
discussions = get_discussions_feed(username, token)
|
| 264 |
|
| 265 |
# Filter by status
|
| 266 |
if filter_status == "open":
|
|
|
|
| 4 |
import sqlite3
|
| 5 |
import json
|
| 6 |
import time
|
| 7 |
+
from datetime import datetime, timezone
|
| 8 |
from flask import Flask, redirect, request, session, render_template, url_for, g
|
| 9 |
import requests
|
| 10 |
|
|
|
|
| 26 |
DB_PATH = "cache.db"
|
| 27 |
|
| 28 |
|
| 29 |
+
def relative_time(iso_timestamp):
|
| 30 |
+
if not iso_timestamp:
|
| 31 |
+
return ""
|
| 32 |
+
try:
|
| 33 |
+
dt = datetime.fromisoformat(iso_timestamp.replace("Z", "+00:00"))
|
| 34 |
+
now = datetime.now(timezone.utc)
|
| 35 |
+
diff = now - dt
|
| 36 |
+
|
| 37 |
+
seconds = diff.total_seconds()
|
| 38 |
+
if seconds < 60:
|
| 39 |
+
return "now"
|
| 40 |
+
elif seconds < 3600:
|
| 41 |
+
mins = int(seconds // 60)
|
| 42 |
+
return f"{mins}m"
|
| 43 |
+
elif seconds < 86400:
|
| 44 |
+
hours = int(seconds // 3600)
|
| 45 |
+
return f"{hours}h"
|
| 46 |
+
elif seconds < 604800:
|
| 47 |
+
days = int(seconds // 86400)
|
| 48 |
+
return f"{days}d"
|
| 49 |
+
elif seconds < 2592000:
|
| 50 |
+
weeks = int(seconds // 604800)
|
| 51 |
+
return f"{weeks}w"
|
| 52 |
+
elif seconds < 31536000:
|
| 53 |
+
months = int(seconds // 2592000)
|
| 54 |
+
return f"{months}mo"
|
| 55 |
+
else:
|
| 56 |
+
years = int(seconds // 31536000)
|
| 57 |
+
return f"{years}y"
|
| 58 |
+
except Exception:
|
| 59 |
+
return ""
|
| 60 |
+
|
| 61 |
+
|
| 62 |
def get_db():
|
| 63 |
if "db" not in g:
|
| 64 |
g.db = sqlite3.connect(DB_PATH)
|
|
|
|
| 160 |
return discussions
|
| 161 |
|
| 162 |
|
| 163 |
+
def get_discussions_feed(username, token=None, logged_in_user=None):
|
| 164 |
spaces = fetch_user_spaces(username, token)
|
| 165 |
|
| 166 |
all_discussions = []
|
|
|
|
| 170 |
|
| 171 |
for d in discussions:
|
| 172 |
owner_responded = d.get("repoOwner", {}).get("isParticipating", False)
|
| 173 |
+
discussion_author = d.get("author", {}).get("name", "")
|
| 174 |
+
is_own_discussion = logged_in_user and discussion_author.lower() == logged_in_user.lower()
|
| 175 |
+
|
| 176 |
base_score = d.get("numComments", 0) + d.get("numReactionUsers", 0) * 2
|
| 177 |
+
score = base_score
|
| 178 |
# Demote if owner has responded
|
| 179 |
+
if owner_responded:
|
| 180 |
+
score -= 100
|
| 181 |
+
# Heavily demote if user started it themselves
|
| 182 |
+
if is_own_discussion:
|
| 183 |
+
score -= 1000
|
| 184 |
all_discussions.append({
|
| 185 |
"space_id": space_id,
|
| 186 |
"space_name": space_id.split("/")[-1] if "/" in space_id else space_id,
|
|
|
|
| 191 |
"author": d.get("author", {}).get("name", "unknown"),
|
| 192 |
"author_avatar": d.get("author", {}).get("avatarUrl", ""),
|
| 193 |
"created_at": d.get("createdAt", ""),
|
| 194 |
+
"relative_time": relative_time(d.get("createdAt", "")),
|
| 195 |
"num_comments": d.get("numComments", 0),
|
| 196 |
"num_reactions": d.get("numReactionUsers", 0),
|
| 197 |
"top_reactions": d.get("topReactions", []),
|
| 198 |
"score": score,
|
| 199 |
"owner_responded": owner_responded,
|
| 200 |
+
"is_own": is_own_discussion,
|
| 201 |
"url": f"https://huggingface.co/spaces/{space_id}/discussions/{d.get('num')}",
|
| 202 |
})
|
| 203 |
|
|
|
|
| 304 |
filter_status = request.args.get("status", "open")
|
| 305 |
|
| 306 |
spaces = fetch_user_spaces(username, token)
|
| 307 |
+
discussions = get_discussions_feed(username, token, logged_in_user=username)
|
| 308 |
|
| 309 |
# Filter by status
|
| 310 |
if filter_status == "open":
|
templates/dashboard.html
CHANGED
|
@@ -256,11 +256,20 @@
|
|
| 256 |
.feed-item.responded {
|
| 257 |
opacity: 0.5;
|
| 258 |
}
|
| 259 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
font-size: 0.625rem;
|
| 261 |
color: #555;
|
| 262 |
margin-left: 0.25rem;
|
| 263 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
</style>
|
| 265 |
</head>
|
| 266 |
<body>
|
|
@@ -320,14 +329,15 @@
|
|
| 320 |
{% if discussions %}
|
| 321 |
<div class="feed">
|
| 322 |
{% for d in discussions %}
|
| 323 |
-
<a href="{{ d.url }}" target="_blank" class="feed-item status-{{ d.status }}{{ ' is-report' if 'report' in d.title|lower else '' }}{{ ' responded' if d.owner_responded else '' }}">
|
| 324 |
<div class="feed-meta">
|
| 325 |
<span class="feed-space">{{ d.space_name }}</span>
|
| 326 |
<span class="feed-type {{ 'pr' if d.is_pr else 'discussion' }}">
|
| 327 |
{{ 'PR' if d.is_pr else 'Discussion' }}
|
| 328 |
</span>
|
| 329 |
<span class="feed-status {{ d.status }}">{{ d.status }}</span>
|
| 330 |
-
{% if d.owner_responded %}<span class="responded-badge">replied</span>{% endif %}
|
|
|
|
| 331 |
</div>
|
| 332 |
<div class="feed-title">{{ d.title }}</div>
|
| 333 |
<div class="feed-stats">
|
|
|
|
| 256 |
.feed-item.responded {
|
| 257 |
opacity: 0.5;
|
| 258 |
}
|
| 259 |
+
.feed-item.is-own {
|
| 260 |
+
opacity: 0.35;
|
| 261 |
+
}
|
| 262 |
+
.responded-badge,
|
| 263 |
+
.own-badge {
|
| 264 |
font-size: 0.625rem;
|
| 265 |
color: #555;
|
| 266 |
margin-left: 0.25rem;
|
| 267 |
}
|
| 268 |
+
.feed-time {
|
| 269 |
+
font-size: 0.75rem;
|
| 270 |
+
color: #444;
|
| 271 |
+
margin-left: auto;
|
| 272 |
+
}
|
| 273 |
</style>
|
| 274 |
</head>
|
| 275 |
<body>
|
|
|
|
| 329 |
{% if discussions %}
|
| 330 |
<div class="feed">
|
| 331 |
{% for d in discussions %}
|
| 332 |
+
<a href="{{ d.url }}" target="_blank" class="feed-item status-{{ d.status }}{{ ' is-report' if 'report' in d.title|lower else '' }}{{ ' responded' if d.owner_responded else '' }}{{ ' is-own' if d.is_own else '' }}">
|
| 333 |
<div class="feed-meta">
|
| 334 |
<span class="feed-space">{{ d.space_name }}</span>
|
| 335 |
<span class="feed-type {{ 'pr' if d.is_pr else 'discussion' }}">
|
| 336 |
{{ 'PR' if d.is_pr else 'Discussion' }}
|
| 337 |
</span>
|
| 338 |
<span class="feed-status {{ d.status }}">{{ d.status }}</span>
|
| 339 |
+
{% if d.is_own %}<span class="own-badge">you</span>{% elif d.owner_responded %}<span class="responded-badge">replied</span>{% endif %}
|
| 340 |
+
{% if d.relative_time %}<span class="feed-time">{{ d.relative_time }}</span>{% endif %}
|
| 341 |
</div>
|
| 342 |
<div class="feed-title">{{ d.title }}</div>
|
| 343 |
<div class="feed-stats">
|