Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
python-version: "3.12"

- name: Cache pip dependencies
Expand Down Expand Up @@ -60,7 +59,6 @@ jobs:
uses: actions/setup-python@v5
with:

python-version: "3.13"
python-version: "3.12"

- name: Cache pip dependencies
Expand Down
45 changes: 41 additions & 4 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ def health_check():
# Blog Generator Endpoint
# -----------------------------
@app.post("/generate-blog")
@limiter.limit("5/minute")
@limiter.limit("15/hour")
async def create_blog(
request: Request,
problem: Problem,
Expand Down Expand Up @@ -638,8 +638,15 @@ async def publish_blog(
# Dashboard Endpoints
# -----------------------------
@app.get("/dashboard/stats")
async def get_dashboard_stats(x_user_email: Optional[str] = Header(default=None)):
user_email = require_user(x_user_email)
async def get_dashboard_stats(
x_user_email: Optional[str] = Header(default=None),
current_user: Annotated[dict[str, Any] | None, Depends(get_optional_user)] = None,
):
if current_user:
user_email = current_user["email"]
else:
user_email = require_user(x_user_email)

user_filter = {"user_email": user_email}

try:
Expand All @@ -651,7 +658,7 @@ async def get_dashboard_stats(x_user_email: Optional[str] = Header(default=None)
{"$group": {"_id": "$platforms", "count": {"$sum": 1}}},
]
platform_cursor = db.problem_info.aggregate(pipeline_platforms)
platform_counts = {doc["_id"]: doc["count"] async for doc in platform_cursor}
platform_counts = [{"name": doc["_id"], "value": doc["count"]} async for doc in platform_cursor]

seven_days_ago = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()
pipeline_week = [
Expand All @@ -667,6 +674,34 @@ async def get_dashboard_stats(x_user_email: Optional[str] = Header(default=None)
week_cursor = db.problem_info.aggregate(pipeline_week)
week_activity = {doc["_id"]: doc["count"] async for doc in week_cursor}

# All time daily activity for the heatmap
pipeline_all_time = [
{"$match": user_filter},
{
"$group": {
"_id": {"$substr": ["$date", 0, 10]},
"count": {"$sum": 1},
}
},
{"$sort": {"_id": 1}},
]
all_time_cursor = db.problem_info.aggregate(pipeline_all_time)
daily_activity = [{"date": doc["_id"], "count": doc["count"], "level": min(doc["count"], 4)} async for doc in all_time_cursor]

# Calculate streak
current_streak = 0
if daily_activity:
dates_set = {doc["date"] for doc in daily_activity}
today = datetime.now(timezone.utc).date()

current_date = today
if current_date.isoformat() not in dates_set:
current_date = today - timedelta(days=1)

while current_date.isoformat() in dates_set:
current_streak += 1
current_date -= timedelta(days=1)

recent_cursor = (
db.problem_info.find(
user_filter,
Expand All @@ -688,6 +723,8 @@ async def get_dashboard_stats(x_user_email: Optional[str] = Header(default=None)
"total_posts": total,
"platform_counts": platform_counts,
"week_activity": week_activity,
"daily_activity": daily_activity,
"current_streak": current_streak,
"recent": recent,
}
except HTTPException:
Expand Down
18 changes: 13 additions & 5 deletions extension/dashboard.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const API_BASE_URL = "https://leetcodeai-backend.onrender.com";
//const API_BASE_URL = "http://localhost:10000";
//const API_BASE_URL = "https://leetcodeai-backend.onrender.com";
const API_BASE_URL = "http://localhost:10000";

//fixes timezone for IST and all non-UTC users
function getLocalDateStr(date) {
Expand Down Expand Up @@ -46,16 +46,24 @@ async function fetchStatsFromBackend(email) {
headers: { 'X-User-Email': email }
});
if (!res.ok) throw new Error('Bad response');
const { total_posts, platform_counts, week_activity, recent } = await res.json();
const { total_posts, platform_counts, week_activity, recent, current_streak } = await res.json();

document.getElementById('totalPosts').textContent = total_posts;
document.getElementById('thisWeek').textContent =
Object.values(week_activity).reduce((a, b) => a + b, 0);
document.getElementById('streakCount').textContent =
calculateStreakFromWeekMap(week_activity) + ' 🔥';
(current_streak !== undefined ? current_streak : calculateStreakFromWeekMap(week_activity)) + ' 🔥';

renderWeekGridFromMap(week_activity);
renderPlatformBarsFromMap(platform_counts);

let countsMap = {};
if (Array.isArray(platform_counts)) {
platform_counts.forEach(item => countsMap[item.name] = item.value);
} else {
countsMap = platform_counts || {};
}
renderPlatformBarsFromMap(countsMap);

renderHistory(recent);
}

Expand Down
Loading