Skip to content

Commit 50e1d09

Browse files
committed
feat(sync): implement recent accepted submissions retrieval.
1 parent 26a78ed commit 50e1d09

14 files changed

Lines changed: 187 additions & 13 deletions

File tree

config/__init__.py

Whitespace-only changes.

config/logging.py

Whitespace-only changes.

config.py renamed to config/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
class Config:
88

99
LEETCODE_SESSION = os.getenv("LEETCODE_SESSION", "")
10+
LEETCODE_USERNAME = os.getenv("LEETCODE_USERNAME", "")
1011
CSRF_TOKEN = os.getenv("CSRF_TOKEN", "")
1112

1213
GITHUB_USERNAME = os.getenv("GITHUB_USERNAME", "")

leetcode/api.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,42 @@
1-
from requests import Session
1+
from config.settings import Config
2+
from leetcode.parser import parse_submissions
3+
from leetcode.queries import (
4+
PROFILE_QUERY,
5+
RECENT_SUBMISSIONS_QUERY,
6+
)
27

38

49
class LeetCodeAPI:
5-
def __init__(self, session: Session):
6-
self.session = session
710

8-
def validate_session(self) -> bool:
9-
response = self.session.get("https://leetcode.com/api/problems/all/")
11+
def __init__(self, client):
12+
self.client = client
1013

11-
return response.status_code == 200
14+
def get_profile(self):
15+
response = self.client.post(
16+
PROFILE_QUERY,
17+
{
18+
"username": Config.LEETCODE_USERNAME
19+
}
20+
)
21+
22+
if "errors" in response:
23+
raise Exception(response["errors"])
24+
25+
return response["data"]["matchedUser"]
26+
27+
def get_recent_submissions(self, limit=15):
28+
29+
response = self.client.post(
30+
RECENT_SUBMISSIONS_QUERY,
31+
{
32+
"username": Config.LEETCODE_USERNAME,
33+
"limit": limit,
34+
},
35+
)
36+
37+
if "errors" in response:
38+
raise Exception(response["errors"])
39+
40+
return parse_submissions(
41+
response["data"]["recentAcSubmissionList"]
42+
)

leetcode/auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from requests import Session
22

3-
from config import Config
3+
from config.settings import Config
44

55

66
class LeetCodeAuth:

leetcode/client.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from requests import Session
2+
from config.settings import Config
3+
4+
5+
class LeetCodeClient:
6+
def __init__(self, session: Session):
7+
self.session = session
8+
9+
def post(self, query: str, variables: dict = None):
10+
payload = {
11+
"query": query,
12+
"variables": variables or {}
13+
}
14+
15+
response = self.session.post(
16+
Config.GRAPHQL_URL,
17+
json=payload,
18+
timeout=Config.REQUEST_TIMEOUT
19+
)
20+
21+
print("=" * 80)
22+
print("Status Code:", response.status_code)
23+
print(response.text)
24+
print("=" * 80)
25+
26+
return response.json()

leetcode/models.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from dataclasses import dataclass
2+
from datetime import datetime
3+
4+
5+
@dataclass(slots=True)
6+
class Submission:
7+
id: str
8+
title: str
9+
slug: str
10+
timestamp: int
11+
12+
@property
13+
def date(self):
14+
return datetime.fromtimestamp(self.timestamp)

leetcode/parser.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from leetcode.models import Submission
2+
3+
4+
def parse_submissions(data):
5+
submissions = []
6+
7+
for item in data:
8+
submissions.append(
9+
Submission(
10+
id=item["id"],
11+
title=item["title"],
12+
slug=item["titleSlug"],
13+
timestamp=int(item["timestamp"])
14+
)
15+
)
16+
17+
return submissions

leetcode/queries.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
PROFILE_QUERY = """
2+
query userProblemsSolved($username: String!) {
3+
matchedUser(username: $username) {
4+
username
5+
6+
submitStatsGlobal {
7+
acSubmissionNum {
8+
difficulty
9+
count
10+
submissions
11+
}
12+
}
13+
}
14+
}
15+
"""
16+
17+
RECENT_SUBMISSIONS_QUERY = """
18+
query recentAcSubmissions($username: String!, $limit: Int!) {
19+
recentAcSubmissionList(
20+
username: $username,
21+
limit: $limit
22+
) {
23+
id
24+
title
25+
titleSlug
26+
timestamp
27+
}
28+
}
29+
"""

main.py

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from rich.console import Console
22
from rich.panel import Panel
3+
from rich.table import Table
34

45
from leetcode.auth import LeetCodeAuth
6+
from leetcode.client import LeetCodeClient
57
from leetcode.api import LeetCodeAPI
68

79
console = Console()
@@ -12,21 +14,73 @@ def banner():
1214
Panel.fit(
1315
"[bold cyan]LeetCode Sync[/bold cyan]\n"
1416
"Automatic LeetCode → GitHub Synchronization",
15-
title="v0.2.0",
17+
title="v0.4.0",
1618
)
1719
)
1820

1921

22+
def display_statistics(stats):
23+
"""Display solved problem statistics."""
24+
25+
table = Table(title="LeetCode Statistics")
26+
27+
table.add_column("Difficulty", style="cyan")
28+
table.add_column("Solved", justify="right", style="green")
29+
30+
for item in stats:
31+
table.add_row(
32+
item["difficulty"],
33+
str(item["count"])
34+
)
35+
36+
console.print(table)
37+
38+
39+
def display_recent_submissions(submissions):
40+
"""Display recent accepted submissions."""
41+
42+
table = Table(title="Recent Accepted Submissions")
43+
44+
table.add_column("ID", style="cyan")
45+
table.add_column("Title")
46+
table.add_column("Date")
47+
48+
for submission in submissions:
49+
table.add_row(
50+
submission.id,
51+
submission.title,
52+
submission.date.strftime("%Y-%m-%d %H:%M"),
53+
)
54+
55+
console.print(table)
56+
57+
2058
def main():
2159
banner()
2260

2361
auth = LeetCodeAuth()
24-
api = LeetCodeAPI(auth.get_session())
62+
session = auth.get_session()
63+
64+
client = LeetCodeClient(session)
65+
api = LeetCodeAPI(client)
66+
67+
# Profile
68+
profile = api.get_profile()
69+
70+
console.print(
71+
f"[bold green]Logged in as:[/bold green] {profile['username']}\n"
72+
)
73+
74+
display_statistics(
75+
profile["submitStatsGlobal"]["acSubmissionNum"]
76+
)
77+
78+
console.print()
79+
80+
# Recent submissions
81+
submissions = api.get_recent_submissions()
2582

26-
if api.validate_session():
27-
console.print("[green]✓ Session is valid[/green]")
28-
else:
29-
console.print("[red]✗ Invalid session[/red]")
83+
display_recent_submissions(submissions)
3084

3185

3286
if __name__ == "__main__":

0 commit comments

Comments
 (0)