11import React , { useState , useEffect , useMemo } from "react" ;
2+ import { LineChart } from "@mui/x-charts/LineChart" ;
23import {
34 Box ,
45 Container ,
@@ -39,26 +40,54 @@ function SummaryTab({}) {
3940 const [ activeIndex , setActiveIndex ] = useState ( 0 ) ;
4041 const routeParams = useParams ( ) ;
4142 const [ summaryStats , setSummaryStats ] = useState ( null ) ;
43+ const [ backupData , setBackupData ] = useState ( null ) ;
44+ const [ fileMetadata , setFileMetadata ] = useState ( null ) ;
4245
4346 useEffect ( ( ) => {
4447 fetch (
4548 `/api/summary_statistics/${ routeParams . courseId } /${ routeParams . assignmentId } /${ routeParams . studentId } ` ,
46- {
47- method : "GET" ,
48- } ,
49+ { method : "GET" }
4950 )
5051 . then ( ( response ) => {
51- if ( ! response . ok ) {
52- throw new Error ( `HTTP error! Status: ${ response . status } ` ) ;
53- }
52+ if ( ! response . ok ) throw new Error ( `HTTP error! Status: ${ response . status } ` ) ;
5453 return response . json ( ) ;
5554 } )
56- . then ( ( responseData ) => {
57- console . log ( "response data" , responseData ) ;
58- setSummaryStats ( responseData ) ;
59- } ) ;
55+ . then ( ( data ) => setSummaryStats ( data ) ) ;
6056 } , [ routeParams ] ) ;
6157
58+ useEffect ( ( ) => {
59+ fetch (
60+ `/api/backups/${ routeParams . courseId } /${ routeParams . assignmentId } /${ routeParams . studentId } `
61+ )
62+ . then ( ( res ) => res . json ( ) )
63+ . then ( ( data ) => setBackupData ( data ) ) ;
64+ } , [ routeParams ] ) ;
65+
66+ useEffect ( ( ) => {
67+ fetch (
68+ `/api/backup_file_metadata/${ routeParams . courseId } /${ routeParams . assignmentId } /${ routeParams . studentId } `
69+ )
70+ . then ( ( res ) => res . json ( ) )
71+ . then ( ( data ) => setFileMetadata ( data ) ) ;
72+ } , [ routeParams ] ) ;
73+
74+ const backupTimestamps = backupData ?. backups . map ( ( b ) => new Date ( b . created ) ) ?? [ ] ;
75+ const xAxis = [ { data : backupTimestamps , scaleType : "time" , label : "Date" } ] ;
76+ const height = 300 ;
77+
78+ const firstFile = fileMetadata ? Object . keys ( fileMetadata . files_to_metadata ) [ 0 ] : null ;
79+ const numLines = firstFile ? fileMetadata . files_to_metadata [ firstFile ] . num_lines : [ ] ;
80+
81+ const numQuestionsSolved = backupData ?. backups . map ( ( b ) =>
82+ b . history . filter ( ( h ) => h . solved ) . length
83+ ) ?? [ ] ;
84+ const numQuestionsUnsolved = backupData ?. backups . map ( ( b ) =>
85+ b . history . filter ( ( h ) => ! h . solved ) . length
86+ ) ?? [ ] ;
87+ const numAttempts = backupData ?. backups . map ( ( b ) =>
88+ b . history . reduce ( ( sum , h ) => sum + ( h . attempts ?? 0 ) , 0 )
89+ ) ?? [ ] ;
90+
6291 const menuItems = useMemo (
6392 ( ) =>
6493 summaryStats
@@ -82,9 +111,7 @@ function SummaryTab({}) {
82111 < StatisticsDashboard
83112 title = "Number of Problems Solved"
84113 tooltip = "Hover over chart for more details"
85- studentValue = {
86- summaryStats . problems_solved_distribution . studentValue
87- }
114+ studentValue = { summaryStats . problems_solved_distribution . studentValue }
88115 data = { summaryStats . problems_solved_distribution . data }
89116 />
90117 ) ,
@@ -96,41 +123,33 @@ function SummaryTab({}) {
96123 < StatisticsDashboard
97124 title = "Number of Backups"
98125 tooltip = "Hover over chart for more details"
99- studentValue = {
100- summaryStats . number_of_backups_distribution . studentValue
101- }
126+ studentValue = { summaryStats . number_of_backups_distribution . studentValue }
102127 data = { summaryStats . number_of_backups_distribution . data }
103128 />
104129 ) ,
105130 } ,
106131 {
107132 text : "Total Time Spent" ,
108133 icon : < AccessTime /> ,
109- tooltip :
110- "Timestamp of last backup minus timestamp of first backup" ,
134+ tooltip : "Timestamp of last backup minus timestamp of first backup" ,
111135 component : (
112136 < StatisticsDashboard
113137 title = "Total Time Spent (min)"
114138 tooltip = "Hover over chart for more details"
115- studentValue = {
116- summaryStats . total_time_spent_distribution . studentValue
117- }
139+ studentValue = { summaryStats . total_time_spent_distribution . studentValue }
118140 data = { summaryStats . total_time_spent_distribution . data }
119141 />
120142 ) ,
121143 } ,
122144 {
123145 text : "Total Active Time Spent" ,
124146 icon : < Timer /> ,
125- tooltip :
126- "Total time spent on task. To compute this, we do not count large gaps in activity." ,
147+ tooltip : "Total time spent on task. To compute this, we do not count large gaps in activity." ,
127148 component : (
128149 < StatisticsDashboard
129150 title = "Total Active Time Spent (min)"
130151 tooltip = "Hover over chart for more details"
131- studentValue = {
132- summaryStats . active_time_spent_distribution . studentValue
133- }
152+ studentValue = { summaryStats . active_time_spent_distribution . studentValue }
134153 data = { summaryStats . active_time_spent_distribution . data }
135154 />
136155 ) ,
@@ -143,18 +162,18 @@ function SummaryTab({}) {
143162 < StatisticsDashboard
144163 title = "Number of Lint Errors"
145164 tooltip = "Hover over chart for more details"
146- studentValue = {
147- summaryStats . lint_errors_distribution . studentValue
148- }
165+ studentValue = { summaryStats . lint_errors_distribution . studentValue }
149166 data = { summaryStats . lint_errors_distribution . data }
150167 />
151168 ) ,
152169 } ,
153170 ]
154171 : [ ] ,
155- [ summaryStats ] ,
172+ [ summaryStats ]
156173 ) ;
157174
175+ const chartsReady = backupData && fileMetadata && backupTimestamps . length > 0 ;
176+
158177 return (
159178 < Container maxWidth = "xl" sx = { { py : 4 } } >
160179 < div
@@ -170,17 +189,12 @@ function SummaryTab({}) {
170189 </ div >
171190
172191 { menuItems . length > 0 ? (
173- // TODO: generalize this left sidebar + main area into a component
192+
174193 < Box sx = { { display : "flex" , gap : 3 , minHeight : "80vh" } } >
175194 { /* Left Sidebar */ }
176195 < Paper
177196 elevation = { 2 }
178- sx = { {
179- width : 240 ,
180- flexShrink : 0 ,
181- borderRadius : 2 ,
182- overflow : "hidden" ,
183- } }
197+ sx = { { width : 240 , flexShrink : 0 , borderRadius : 2 , overflow : "hidden" } }
184198 >
185199 < List >
186200 { menuItems . map ( ( item , index ) => (
@@ -196,9 +210,7 @@ function SummaryTab({}) {
196210 onClick = { ( e ) => e . stopPropagation ( ) }
197211 sx = { { ml : "auto" , display : "flex" , alignItems : "center" } }
198212 >
199- { item . tooltip ? (
200- < InfoTooltip info = { item . tooltip } />
201- ) : null }
213+ { item . tooltip ? < InfoTooltip info = { item . tooltip } /> : null }
202214 </ Box >
203215 </ ListItemButton >
204216 </ ListItem >
@@ -209,14 +221,35 @@ function SummaryTab({}) {
209221 { /* Main Content Area */ }
210222 < Paper
211223 elevation = { 1 }
212- sx = { {
213- flexGrow : 1 ,
214- p : 4 ,
215- borderRadius : 2 ,
216- backgroundColor : "#fafafa" ,
217- } }
224+ sx = { { flexGrow : 1 , p : 4 , borderRadius : 2 , backgroundColor : "#fafafa" } }
218225 >
219226 { menuItems [ activeIndex ] . component }
227+
228+ { chartsReady ? (
229+ < >
230+ < LineChart
231+ xAxis = { xAxis }
232+ series = { [ { curve : "linear" , data : numLines , label : `# of lines in ${ firstFile } ` } ] }
233+ height = { height }
234+ />
235+ < LineChart
236+ margin = { { top : 100 } }
237+ xAxis = { xAxis }
238+ series = { [
239+ { curve : "linear" , data : numQuestionsSolved , label : "# of questions solved" } ,
240+ { curve : "linear" , data : numQuestionsUnsolved , label : "# of questions unsolved" } ,
241+ ] }
242+ height = { height }
243+ />
244+ < LineChart
245+ xAxis = { xAxis }
246+ series = { [ { curve : "linear" , data : numAttempts , label : "# of attempts" } ] }
247+ height = { height }
248+ />
249+ </ >
250+ ) : (
251+ < CircularProgress />
252+ ) }
220253 </ Paper >
221254 </ Box >
222255 ) : (
0 commit comments