DEV Community

Naman
Naman

Posted on

πŸ“Š Extracting DSA Question Statistics from Codolio and TakeUForward (TUF)

If you're actively solving DSA problems across multiple platforms (LeetCode, GeeksforGeeks, Codeforces, TakeUForward) and also contributing to GitHub, wouldn't it be amazing to have one unified dashboard showing all your coding stats? In this tutorial, I'll show you how I built a Complete Coding Stats Aggregator that combines:

  • DSA Stats from Codolio (LeetCode, GFG, Codeforces, etc.)
  • TakeUForward progress separately
  • GitHub Activity (commits, stars, PRs, active days)

All exported to a single coding-stats.json file!

🎯 What We're Building

A Python script that:

  • βœ… Fetches coding stats from Codolio API (all platforms except TUF)
  • βœ… Fetches TakeUForward stats separately (to avoid duplication)
  • βœ… Fetches GitHub activity stats from Codolio's GitHub proxy
  • βœ… Combines everything into coding-stats.json
  • βœ… Categorizes DSA problems by difficulty: Easy, Medium, Hard

πŸ” Step 1: Reverse Engineering the APIs

1.1 Finding the Codolio API

Goal: Get your DSA stats from all platforms (except TUF)

  1. Open Codolio Profile: Go to codolio.com and log in
  2. Open Developer Tools:
    • Windows/Linux: F12 or Ctrl+Shift+I
    • Mac: Cmd+Option+I
  3. Go to Network Tab: Click on the "Network" tab
  4. Reload the Page: Press Ctrl+R (Windows/Linux) or Cmd+R (Mac)
  5. Filter Requests: Type "user" in the filter box
  6. Locate the API Call: Look for a request to https://api.codolio.com/user
  7. Extract Headers:
    • Click on the request
    • Go to "Headers" section
    • Find authorization header
    • Copy the entire Bearer token (starts with Bearer eyJ...)

API Endpoint: https://api.codolio.com/user

What you need:

  • Authorization token (Bearer token from headers)

1.2 Finding the TakeUForward API

Goal: Get your TUF-specific stats

  1. Open Your TUF Profile: Visit https://takeuforward.org/profile/YOUR_USERNAME
  2. Open Developer Tools: F12
  3. Go to Network Tab: Click "Network"
  4. Reload Page: Ctrl+R
  5. Filter: Type "dsa-progress"
  6. Find the Endpoint: Look for:
   https://backend-go.takeuforward.org/api/v1/shared/profile/dsa-progress/YOUR_USERNAME
Enter fullscreen mode Exit fullscreen mode

API Endpoint: https://backend-go.takeuforward.org/api/v1/shared/profile/dsa-progress/{username}

What you need:

  • Your TUF username (no authentication required!)

1.3 Finding the GitHub Stats API (via Codolio)

Goal: Get GitHub activity stats

  1. On Codolio Profile: Make sure your GitHub is connected
  2. Open Developer Tools: F12
  3. Network Tab: Click "Network"
  4. Navigate to GitHub Section: Click on your GitHub stats in Codolio
  5. Filter: Type "github"
  6. Find the Endpoint: Look for:
   https://api.codolio.com/github/profile?userKey=YOUR_PROFILE_NAME
Enter fullscreen mode Exit fullscreen mode

API Endpoint: https://api.codolio.com/github/profile?userKey={profileName}

What you need:

  • Your Codolio profile name (username)

πŸ› οΈ Step 2: Understanding the Data Structures

2.1 Codolio Response (DSA Stats)

{
  "data": {
    "platformProfiles": {
      "platformProfiles": [
        {
          "platform": "leetcode",
          "totalQuestionStats": {
            "easyQuestionCounts": 150,
            "mediumQuestionCounts": 200,
            "hardQuestionCounts": 50,
            "totalQuestionCounts": 400
          }
        },
        {
          "platform": "gfg",
          "totalQuestionStats": {
            "basicQuestionCounts": 100,
            "schoolQuestionCounts": 50,
            "easyQuestionCounts": 120,
            "mediumQuestionCounts": 80,
            "hardQuestionCounts": 30,
            "totalQuestionCounts": 380
          }
        },
        {
          "platform": "tuf",
          "totalQuestionStats": { /* we skip this */ }
        }
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • Contains multiple platforms in one response
  • Some platforms (like GFG) have basicQuestionCounts and schoolQuestionCounts β†’ treat as Easy
  • TUF is included but we skip it to avoid double-counting

2.2 TakeUForward Response

{
  "data": {
    "data": {
      "solvedEasy": 45,
      "solvedMedium": 60,
      "solvedHard": 15,
      "totalSolved": 120
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

2.3 GitHub Response (via Codolio)

{
  "data": {
    "githubProfile": "naman08",
    "stars": 42,
    "issues": 15,
    "totalActiveDays": 365,
    "pushRequestsCount": 89,
    "totalContributions": 1247
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ’» Step 3: Writing the Complete Aggregator Script

3.1 Install Required Package

pip install requests
Enter fullscreen mode Exit fullscreen mode

3.2 The Complete Script

Create a file aggregator.py:

import requests
import json
import os

# =========================
# 1️⃣ CODOLIO (ALL EXCEPT TUF)
# =========================
codolio_url = "https://api.codolio.com/user"

codolio_headers = {
    "authorization": "Bearer YOUR_TOKEN_HERE",  # πŸ”₯ REPLACE THIS
    "accept": "*/*",
    "referer": "https://codolio.com/"
}

res = requests.get(codolio_url, headers=codolio_headers)

if res.status_code != 200:
    print("❌ Codolio Error:", res.status_code)
    exit()

data = res.json()
platforms = data["data"]["platformProfiles"]["platformProfiles"]

codolio_easy = codolio_medium = codolio_hard = codolio_total = 0

for p in platforms:
    if p.get("platform") == "tuf":
        continue  # ❌ Skip TUF to avoid duplicates

    q = p.get("totalQuestionStats")
    if not q:
        continue

    # Standard difficulty counts
    codolio_easy += q.get("easyQuestionCounts") or 0
    codolio_medium += q.get("mediumQuestionCounts") or 0
    codolio_hard += q.get("hardQuestionCounts") or 0

    # Platform-specific easy categories (GFG, etc.)
    # Treat "basic" and "school" level as Easy
    codolio_easy += q.get("basicQuestionCounts") or 0
    codolio_easy += q.get("schoolQuestionCounts") or 0

    codolio_total += q.get("totalQuestionCounts") or 0

print(f"βœ… Codolio: {codolio_total} problems")


# =========================
# 2️⃣ TUF OFFICIAL API
# =========================
tuf_url = "https://backend-go.takeuforward.org/api/v1/shared/profile/dsa-progress/YOUR_USERNAME"  # πŸ”₯ REPLACE

tuf_headers = {
    "accept": "application/json, text/plain, */*",
    "origin": "https://takeuforward.org",
    "referer": "https://takeuforward.org/"
}

res = requests.get(tuf_url, headers=tuf_headers)

if res.status_code != 200:
    print("❌ TUF Error:", res.status_code)
    exit()

tuf_data = res.json()["data"]["data"]

tuf_easy = tuf_data["solvedEasy"]
tuf_medium = tuf_data["solvedMedium"]
tuf_hard = tuf_data["solvedHard"]
tuf_total = tuf_easy + tuf_medium + tuf_hard

print(f"βœ… TUF: {tuf_total} problems")


# =========================
# 3️⃣ FINAL DSA COMBINED
# =========================
final_easy = codolio_easy + tuf_easy
final_medium = codolio_medium + tuf_medium
final_hard = codolio_hard + tuf_hard
final_total = final_easy + final_medium + final_hard

print(f"πŸ“Š Total DSA: {final_total} problems")


# =========================
# 4️⃣ GITHUB STATS (FROM CODOLIO)
# =========================
github_url = "https://api.codolio.com/github/profile?userKey=YOUR_PROFILE_NAME"  # πŸ”₯ REPLACE

github_headers = {
    "accept": "*/*",
    "referer": "https://codolio.com/"
}

try:
    res = requests.get(github_url, headers=github_headers, timeout=15)

    if res.status_code != 200:
        raise Exception(f"GitHub API error {res.status_code}")

    github_json = res.json()
    gh = github_json.get("data", {})  # βœ… Extract the "data" object

    github_data = {
        "githubProfile": gh.get("githubProfile"),
        "stars": gh.get("stars", 0),
        "issues": gh.get("issues", 0),
        "totalActiveDays": gh.get("totalActiveDays", 0),
        "pushRequestsCount": gh.get("pushRequestsCount", 0),
        "commits": gh.get("totalContributions", 0)
    }

    print(f"βœ… GitHub: {github_data['commits']} commits")

except Exception as e:
    print(f"⚠️ GitHub fetch failed: {e}")
    github_data = {}


# =========================
# 5️⃣ COMBINE & EXPORT JSON
# =========================
output = {
    "codolio": {
        "easy": codolio_easy,
        "medium": codolio_medium,
        "hard": codolio_hard,
        "total": codolio_total
    },
    "tuf": {
        "easy": tuf_easy,
        "medium": tuf_medium,
        "hard": tuf_hard,
        "total": tuf_total
    },
    "final": {
        "easy": final_easy,
        "medium": final_medium,
        "hard": final_hard,
        "total": final_total
    },
    "github": github_data
}

# Create public directory if it doesn't exist
os.makedirs("public", exist_ok=True)

with open("public/coding-stats.json", "w") as f:
    json.dump(output, f, indent=2)

print("\nπŸŽ‰ coding-stats.json generated successfully!")
print(f"πŸ“ Location: public/coding-stats.json")
Enter fullscreen mode Exit fullscreen mode

3.3 Replace Placeholders

Before running, replace:

  1. Line 10: YOUR_TOKEN_HERE β†’ Your Codolio Bearer token
  2. Line 44: YOUR_USERNAME β†’ Your TakeUForward username
  3. Line 73: YOUR_PROFILE_NAME β†’ Your Codolio profile name (e.g., naman08)

πŸ“€ Step 4: Run the Script

python aggregator.py
Enter fullscreen mode Exit fullscreen mode

Expected Output:

βœ… Codolio: 630 problems
βœ… TUF: 120 problems
πŸ“Š Total DSA: 750 problems
βœ… GitHub: 1247 commits

πŸŽ‰ coding-stats.json generated successfully!
πŸ“ Location: public/coding-stats.json
Enter fullscreen mode Exit fullscreen mode

Generated File (public/coding-stats.json):

{
  "codolio": {
    "easy": 270,
    "medium": 280,
    "hard": 80,
    "total": 630
  },
  "tuf": {
    "easy": 45,
    "medium": 60,
    "hard": 15,
    "total": 120
  },
  "final": {
    "easy": 315,
    "medium": 340,
    "hard": 95,
    "total": 750
  },
  "github": {
    "githubProfile": "naman08",
    "stars": 42,
    "issues": 15,
    "totalActiveDays": 365,
    "pushRequestsCount": 89,
    "commits": 1247
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”„ Step 5: Automate with GitHub Actions

Create .github/workflows/update-stats.yml:

name: Update Coding Stats

on:
  schedule:
    - cron: '0 0 * * *'  # Run daily at midnight UTC
  workflow_dispatch:  # Allow manual trigger

jobs:
  update-stats:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.10'

      - name: Install dependencies
        run: pip install requests

      - name: Run aggregator script
        env:
          CODOLIO_TOKEN: ${{ secrets.CODOLIO_TOKEN }}
          TUF_USERNAME: ${{ secrets.TUF_USERNAME }}
          CODOLIO_PROFILE: ${{ secrets.CODOLIO_PROFILE }}
        run: |
          # Replace placeholders in script
          sed -i "s/YOUR_TOKEN_HERE/$CODOLIO_TOKEN/g" aggregator.py
          sed -i "s/YOUR_USERNAME/$TUF_USERNAME/g" aggregator.py
          sed -i "s/YOUR_PROFILE_NAME/$CODOLIO_PROFILE/g" aggregator.py

          # Run the script
          python aggregator.py

      - name: Commit and push changes
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add public/coding-stats.json
          git diff --staged --quiet || git commit -m "πŸ“Š Update coding stats [$(date +'%Y-%m-%d')]"
          git push
Enter fullscreen mode Exit fullscreen mode

Add GitHub Secrets

Go to your repository β†’ Settings β†’ Secrets and variables β†’ Actions β†’ New repository secret:

  1. CODOLIO_TOKEN: Your Bearer token (without "Bearer " prefix)
  2. TUF_USERNAME: Your TakeUForward username
  3. CODOLIO_PROFILE: Your Codolio profile name

🎯 Conclusion

You now have a fully automated coding stats aggregator that combines:

  • βœ… Multi-platform DSA progress (via Codolio + TUF)
  • βœ… GitHub activity metrics
  • βœ… Auto-updates via GitHub Actions
  • βœ… Ready-to-display JSON format

Perfect for portfolios, resumes, or tracking your coding journey!


Found this helpful? Star the repo and share with fellow developers! 🌟

Happy Coding! πŸš€

Top comments (0)