Skip to content

Commit d3c6f8f

Browse files
committed
add submission calendar
1 parent 35558ba commit d3c6f8f

File tree

11 files changed

+316
-114
lines changed

11 files changed

+316
-114
lines changed

jupyterlab_leetcode/handlers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .leetcode_handler import (CreateNotebookHandler, LeetCodeProfileHandler,
66
LeetCodeQuestionHandler,
77
LeetCodeStatisticsHandler,
8+
LeetCodeSubmissionCalendarHandlar,
89
LeetCodeWebSocketSubmitHandler,
910
SubmitNotebookHandler)
1011

@@ -20,6 +21,7 @@ def setup_handlers(web_app):
2021
CreateNotebookHandler,
2122
SubmitNotebookHandler,
2223
LeetCodeWebSocketSubmitHandler,
24+
LeetCodeSubmissionCalendarHandlar,
2325
]
2426

2527
web_app.add_handlers(

jupyterlab_leetcode/handlers/leetcode_handler.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,41 @@ async def get(self):
189189
self.finish(res)
190190

191191

192+
class LeetCodeSubmissionCalendarHandlar(LeetCodeHandler):
193+
route = r"leetcode/submission"
194+
195+
@tornado.web.authenticated
196+
async def get(self):
197+
username = self.get_query_argument("username", "", strip=True)
198+
if not username:
199+
self.set_status(400)
200+
self.finish(json.dumps({"message": "Username parameter is required"}))
201+
return
202+
await self.graphql(
203+
name="submission_calendar",
204+
query={
205+
"query": """query userProfileCalendar($username: String!, $year: Int) {
206+
matchedUser(username: $username) {
207+
userCalendar(year: $year) {
208+
activeYears
209+
streak
210+
totalActiveDays
211+
dccBadges {
212+
timestamp
213+
badge {
214+
name
215+
icon
216+
}
217+
}
218+
submissionCalendar
219+
}
220+
}
221+
}""",
222+
"variables": {"username": username},
223+
},
224+
)
225+
226+
192227
class LeetCodeQuestionHandler(LeetCodeHandler):
193228
route = r"leetcode/questions"
194229

src/components/Actions.tsx

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
Anchor,
3+
Center,
34
Paper,
45
PaperProps,
56
Stack,
@@ -43,23 +44,25 @@ const Actions: React.FC<{
4344
paperProps: PaperProps;
4445
}> = ({ paperProps }) => {
4546
return (
46-
<Paper {...paperProps}>
47-
<Stack gap="xs">
48-
{...Data.map(item => (
49-
<Tooltip label={item.label} key={item.label}>
50-
<Anchor
51-
size="xs"
52-
target="_blank"
53-
underline="never"
54-
href={item.href}
55-
>
56-
<ThemeIcon size="sm" variant="white">
57-
{item.icon}
58-
</ThemeIcon>
59-
</Anchor>
60-
</Tooltip>
61-
))}
62-
</Stack>
47+
<Paper {...paperProps} style={{ alignContent: 'center' }}>
48+
<Center>
49+
<Stack gap="xs">
50+
{Data.map(item => (
51+
<Tooltip label={item.label} key={item.label}>
52+
<Anchor
53+
size="xs"
54+
target="_blank"
55+
underline="never"
56+
href={item.href}
57+
>
58+
<ThemeIcon size="sm" variant="white">
59+
{item.icon}
60+
</ThemeIcon>
61+
</Anchor>
62+
</Tooltip>
63+
))}
64+
</Stack>
65+
</Center>
6366
</Paper>
6467
);
6568
};

src/components/LeetCodeMain.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Profile from './Profile';
88
import Statistics from './Statistics';
99
import QuestionTable from './QuestionTable';
1010
import Actions from './Actions';
11+
import SubmissionCalendar from './SubmissionCalendar';
1112

1213
const MainHeaderPaperProps: PaperProps = {
1314
shadow: 'md',
@@ -55,13 +56,20 @@ const LeetCodeMain: React.FC<{ docManager: IDocumentManager }> = ({
5556
return (
5657
<Container fluid={true} h="100%" p="lg" id="jll-main">
5758
<Stack>
58-
<Group id="jll-profile" align="stretch">
59-
<Profile paperProps={MainHeaderPaperProps} profile={profile} />
59+
<Group id="jll-profile" wrap="nowrap" align="stretch">
60+
<Profile
61+
paperProps={{ ...MainHeaderPaperProps, w: '15%' }}
62+
profile={profile}
63+
/>
6064
<Statistics
61-
paperProps={MainHeaderPaperProps}
65+
paperProps={{ ...MainHeaderPaperProps, w: '30%' }}
66+
username={profile?.username}
67+
/>
68+
<SubmissionCalendar
69+
paperProps={{ ...MainHeaderPaperProps, w: '50%' }}
6270
username={profile?.username}
6371
/>
64-
<Actions paperProps={MainHeaderPaperProps} />
72+
<Actions paperProps={{ ...MainHeaderPaperProps, w: '5%' }} />
6573
</Group>
6674
<QuestionTable openNotebook={openNoteBook} height={calcHeight()} />
6775
</Stack>

src/components/NotebookToolbar.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,10 @@ const LeetCodeNotebookToolbar: React.FC<{ notebook: NotebookPanel }> = ({
7979
};
8080

8181
const getResultCell = () => {
82-
let resultCellModel: ICellModel | null = null;
8382
const cells = notebook.content.model?.cells ?? [];
84-
for (const cell of cells) {
85-
if (cell.metadata['id'] === 'result') {
86-
resultCellModel = cell;
87-
}
88-
}
83+
let resultCellModel = Array.from(cells).find(
84+
c => c.metadata['id'] === 'result'
85+
);
8986
if (!resultCellModel) {
9087
const activeCellIdx = cells.length ? cells.length - 1 : 0;
9188
notebook.content.activeCellIndex = activeCellIdx;

src/components/Profile.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,10 @@ const Profile: React.FC<{
77
paperProps: PaperProps;
88
}> = ({ profile, paperProps }) => {
99
return (
10-
<Paper
11-
miw="20%"
12-
maw="40%"
13-
{...paperProps}
14-
style={{ alignContent: 'center' }}
15-
>
10+
<Paper {...paperProps} style={{ alignContent: 'center' }}>
1611
<Center>
1712
<Stack gap={0}>
18-
<Avatar src={profile?.avatar} size="md" radius="xl" mx="auto" />
13+
<Avatar src={profile?.avatar} size="lg" radius="xl" mx="auto" />
1914
<Text ta="center" fz="md" fw={500} mt="xs">
2015
{profile?.realName}
2116
</Text>

src/components/Statistics.tsx

Lines changed: 82 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -106,85 +106,88 @@ const Statistics: React.FC<{
106106
const getTotalCount = () => all?.get('all') || 0;
107107

108108
return (
109-
<Paper {...paperProps}>
110-
<Group>
111-
<RingProgress
112-
size={120}
113-
thickness={8}
114-
roundCaps
115-
sections={
116-
isHovering && difficultySections
117-
? difficultySections
118-
: getProgressSections()
119-
}
120-
label={
121-
<Center>
122-
<Stack gap={0} align="center">
123-
{isHovering ? (
124-
<>
125-
<Text fz="xs">Beats</Text>
126-
<Group gap={0}>
127-
<Text fw="bolder" fz="lg">
128-
{Math.floor(beats ?? totalBeats)}
129-
</Text>
130-
<Text fz="sm">
131-
.{(beats ?? totalBeats).toFixed(2).split('.')[1]}%
132-
</Text>
133-
</Group>
134-
</>
135-
) : (
136-
<>
137-
<Group gap={0}>
138-
<Text fw="bolder" fz="lg">
139-
{getTotalAc()}
140-
</Text>
141-
<Text fz="sm">/{getTotalCount()}</Text>
142-
</Group>
143-
<Group gap={0}>
144-
<IconCheck
145-
size={12}
146-
stroke={1.5}
147-
color={StatusColors['SOLVED']}
148-
/>
149-
<Text fz="xs">Solved</Text>
150-
</Group>
151-
</>
152-
)}
153-
</Stack>
154-
</Center>
155-
}
156-
onMouseOver={() => setIsHovering(true)}
157-
onMouseLeave={() => setIsHovering(false)}
158-
/>
159-
<Stack gap="xs" w="5em">
160-
{Object.entries(DifficultyColors).map(([d, c]) => (
161-
<DifficultyStatistics
162-
key={d}
163-
text={d}
164-
color={c}
165-
solved={accepted?.get(d) ?? 0}
166-
total={all?.get(d) ?? 0}
167-
onHover={() => {
168-
setIsHovering(true);
169-
setBeats(difficultyBeats?.get(d) ?? 0);
170-
setDifficultySections([
171-
{
172-
value: Math.round(
173-
((accepted?.get(d) ?? 0) / (all?.get(d) ?? 0)) * 100
174-
),
175-
color: c
176-
}
177-
]);
178-
}}
179-
onLeave={() => {
180-
setIsHovering(false);
181-
setBeats(null);
182-
setDifficultySections(null);
183-
}}
184-
/>
185-
))}
186-
</Stack>
187-
</Group>
109+
<Paper {...paperProps} style={{ alignContent: 'center' }}>
110+
<Center>
111+
<Group>
112+
<RingProgress
113+
size={120}
114+
thickness={8}
115+
roundCaps
116+
transitionDuration={250}
117+
sections={
118+
isHovering && difficultySections
119+
? difficultySections
120+
: getProgressSections()
121+
}
122+
label={
123+
<Center>
124+
<Stack gap={0} align="center">
125+
{isHovering ? (
126+
<>
127+
<Text fz="xs">Beats</Text>
128+
<Group gap={0}>
129+
<Text fw="bolder" fz="lg">
130+
{Math.floor(beats ?? totalBeats)}
131+
</Text>
132+
<Text fz="sm">
133+
.{(beats ?? totalBeats).toFixed(2).split('.')[1]}%
134+
</Text>
135+
</Group>
136+
</>
137+
) : (
138+
<>
139+
<Group gap={0}>
140+
<Text fw="bolder" fz="lg">
141+
{getTotalAc()}
142+
</Text>
143+
<Text fz="xs">/{getTotalCount()}</Text>
144+
</Group>
145+
<Group gap={0}>
146+
<IconCheck
147+
size={12}
148+
stroke={1.5}
149+
color={StatusColors['SOLVED']}
150+
/>
151+
<Text fz="xs">Solved</Text>
152+
</Group>
153+
</>
154+
)}
155+
</Stack>
156+
</Center>
157+
}
158+
onMouseOver={() => setIsHovering(true)}
159+
onMouseLeave={() => setIsHovering(false)}
160+
/>
161+
<Stack gap="xs" w="5em">
162+
{Object.entries(DifficultyColors).map(([d, c]) => (
163+
<DifficultyStatistics
164+
key={d}
165+
text={d}
166+
color={c}
167+
solved={accepted?.get(d) ?? 0}
168+
total={all?.get(d) ?? 0}
169+
onHover={() => {
170+
setIsHovering(true);
171+
setBeats(difficultyBeats?.get(d) ?? 0);
172+
setDifficultySections([
173+
{
174+
value: Math.round(
175+
((accepted?.get(d) ?? 0) / (all?.get(d) ?? 0)) * 100
176+
),
177+
color: c
178+
}
179+
]);
180+
}}
181+
onLeave={() => {
182+
setIsHovering(false);
183+
setBeats(null);
184+
setDifficultySections(null);
185+
}}
186+
/>
187+
))}
188+
</Stack>
189+
</Group>
190+
</Center>
188191
</Paper>
189192
);
190193
};

0 commit comments

Comments
 (0)