@@ -55,18 +55,12 @@ const getUserStatistics = async (req, res) => {
5555 type : sequelize . QueryTypes . SELECT
5656 } ) ;
5757
58- // Get study hours for the last 7 days
59- const oneWeekAgo = new Date ( ) ;
60- oneWeekAgo . setDate ( oneWeekAgo . getDate ( ) - 7 ) ;
61-
58+ // Get all study hours (all-time statistics)
6259 const studySessions = await StudySession . findAll ( {
6360 where : {
64- user_id : userId ,
65- study_date : {
66- [ Op . gte ] : oneWeekAgo
67- }
61+ user_id : userId
6862 } ,
69- order : [ [ 'study_date' , 'ASC ' ] ]
63+ order : [ [ 'study_date' , 'DESC ' ] ]
7064 } ) ;
7165
7266 // Get aggregate statistics
@@ -162,6 +156,212 @@ const getUserStatistics = async (req, res) => {
162156 }
163157} ;
164158
159+ /**
160+ * Get quiz performance statistics only
161+ */
162+ const getQuizPerformance = async ( req , res ) => {
163+ try {
164+ const userId = req . user . id ;
165+
166+ console . log ( '[LOG statistics] ========= Fetching quiz performance for user:' , userId ) ;
167+
168+ // Get quiz progress statistics
169+ const quizProgress = await QuizProgress . findAll ( {
170+ where : { user_id : userId } ,
171+ include : [
172+ {
173+ model : Quiz ,
174+ as : 'quiz' ,
175+ attributes : [ 'title' ]
176+ }
177+ ] ,
178+ order : [ [ 'updated_at' , 'DESC' ] ]
179+ } ) ;
180+
181+ // Get quiz aggregate statistics
182+ const [ aggregateStats ] = await sequelize . query ( `
183+ SELECT
184+ COUNT(DISTINCT qp.quiz_id) as total_quizzes_attempted,
185+ COALESCE(AVG(qp.progress), 0) as avg_quiz_progress,
186+ COALESCE(AVG(qp.best_score), 0) as avg_quiz_score,
187+ COALESCE(MAX(qp.best_score), 0) as highest_score,
188+ COALESCE(MIN(qp.best_score), 0) as lowest_score
189+ FROM quiz_progress qp
190+ WHERE qp.user_id = ?
191+ ` , {
192+ replacements : [ userId ] ,
193+ type : sequelize . QueryTypes . SELECT
194+ } ) ;
195+
196+ const formattedQuizProgress = quizProgress . map ( qp => ( {
197+ quizId : qp . quiz_id ,
198+ quizTitle : qp . quiz ?. title || 'Unknown Quiz' ,
199+ progress : qp . progress ,
200+ bestScore : qp . best_score ,
201+ attemptsCount : qp . attempts_count ,
202+ updatedAt : qp . updated_at
203+ } ) ) ;
204+
205+ res . json ( {
206+ quiz_progress : formattedQuizProgress ,
207+ summary : {
208+ total_quizzes_attempted : aggregateStats . total_quizzes_attempted || 0 ,
209+ avg_quiz_progress : parseFloat ( aggregateStats . avg_quiz_progress || 0 ) . toFixed ( 2 ) ,
210+ avg_quiz_score : parseFloat ( aggregateStats . avg_quiz_score || 0 ) . toFixed ( 2 ) ,
211+ highest_score : aggregateStats . highest_score || 0 ,
212+ lowest_score : aggregateStats . lowest_score || 0
213+ }
214+ } ) ;
215+ } catch ( error ) {
216+ console . error ( '[LOG statistics] ========= Error fetching quiz performance:' , error ) ;
217+ res . status ( 500 ) . json ( {
218+ message : 'Error fetching quiz performance' ,
219+ error : error . message
220+ } ) ;
221+ }
222+ } ;
223+
224+ /**
225+ * Get note progress statistics only
226+ */
227+ const getNoteProgress = async ( req , res ) => {
228+ try {
229+ const userId = req . user . id ;
230+
231+ console . log ( '[LOG statistics] ========= Fetching note progress for user:' , userId ) ;
232+
233+ // Get note progress statistics
234+ const noteProgress = await sequelize . query ( `
235+ SELECT np.*, n.title
236+ FROM note_progress np
237+ JOIN notes n ON np.note_id = n.id
238+ WHERE np.user_id = ?
239+ ORDER BY np.updated_at DESC
240+ ` , {
241+ replacements : [ userId ] ,
242+ type : sequelize . QueryTypes . SELECT
243+ } ) ;
244+
245+ // Get note aggregate statistics
246+ const [ aggregateStats ] = await sequelize . query ( `
247+ SELECT
248+ COUNT(DISTINCT np.note_id) as total_notes_read,
249+ COALESCE(AVG(np.progress), 0) as avg_note_progress
250+ FROM note_progress np
251+ WHERE np.user_id = ?
252+ ` , {
253+ replacements : [ userId ] ,
254+ type : sequelize . QueryTypes . SELECT
255+ } ) ;
256+
257+ const formattedNoteProgress = noteProgress && Array . isArray ( noteProgress ) ?
258+ noteProgress . map ( np => ( {
259+ noteId : np . note_id ,
260+ noteTitle : np . title || 'Unknown Note' ,
261+ progress : np . progress ,
262+ updatedAt : np . updated_at
263+ } ) ) : [ ] ;
264+
265+ res . json ( {
266+ note_progress : formattedNoteProgress ,
267+ summary : {
268+ total_notes_read : aggregateStats . total_notes_read || 0 ,
269+ avg_note_progress : parseFloat ( aggregateStats . avg_note_progress || 0 ) . toFixed ( 2 )
270+ }
271+ } ) ;
272+ } catch ( error ) {
273+ console . error ( '[LOG statistics] ========= Error fetching note progress:' , error ) ;
274+ res . status ( 500 ) . json ( {
275+ message : 'Error fetching note progress' ,
276+ error : error . message
277+ } ) ;
278+ }
279+ } ;
280+
281+ /**
282+ * Get study sessions only
283+ */
284+ const getStudySessions = async ( req , res ) => {
285+ try {
286+ const userId = req . user . id ;
287+ const { days } = req . query ; // Optional days filter
288+
289+ console . log ( '[LOG statistics] ========= Fetching study sessions for user:' , userId ) ;
290+
291+ let whereClause = { user_id : userId } ;
292+
293+ // Only apply date filter if days parameter is provided
294+ if ( days && parseInt ( days ) > 0 ) {
295+ const startDate = new Date ( ) ;
296+ startDate . setDate ( startDate . getDate ( ) - parseInt ( days ) ) ;
297+ whereClause . study_date = { [ Op . gte ] : startDate } ;
298+ }
299+
300+ const studySessions = await StudySession . findAll ( {
301+ where : whereClause ,
302+ order : [ [ 'study_date' , 'DESC' ] ]
303+ } ) ;
304+
305+ // Get study session aggregate statistics
306+ let aggregateQuery = `
307+ SELECT
308+ COALESCE(SUM(ss.hours), 0) as total_study_hours,
309+ COALESCE(AVG(ss.hours), 0) as avg_daily_hours,
310+ COALESCE(AVG(ss.productivity_score), 0) as avg_productivity_score,
311+ COUNT(*) as total_sessions
312+ FROM study_sessions ss
313+ WHERE ss.user_id = ?
314+ ` ;
315+
316+ let replacements = [ userId ] ;
317+
318+ // Add date filter if days parameter is provided
319+ if ( days && parseInt ( days ) > 0 ) {
320+ const startDate = new Date ( ) ;
321+ startDate . setDate ( startDate . getDate ( ) - parseInt ( days ) ) ;
322+ aggregateQuery += ` AND ss.study_date >= ?` ;
323+ replacements . push ( startDate ) ;
324+ }
325+
326+ const [ aggregateStats ] = await sequelize . query ( aggregateQuery , {
327+ replacements,
328+ type : sequelize . QueryTypes . SELECT
329+ } ) ;
330+
331+ const formattedStudySessions = studySessions . map ( ss => {
332+ // Calculate productivity change (simplified)
333+ const productivityChange = ss . productivity_score ?
334+ ( ss . productivity_score - ( ss . previousDayScore || ss . productivity_score ) ) :
335+ 0 ;
336+
337+ return {
338+ date : ss . study_date ,
339+ hours : ss . hours ,
340+ productivityScore : ss . productivity_score || 0 ,
341+ productivityChange,
342+ notes : ss . notes ,
343+ updatedAt : ss . updated_at
344+ } ;
345+ } ) ;
346+
347+ res . json ( {
348+ study_hours : formattedStudySessions ,
349+ summary : {
350+ total_study_hours : aggregateStats . total_study_hours || 0 ,
351+ avg_daily_hours : parseFloat ( aggregateStats . avg_daily_hours || 0 ) . toFixed ( 2 ) ,
352+ avg_productivity_score : parseFloat ( aggregateStats . avg_productivity_score || 0 ) . toFixed ( 2 ) ,
353+ total_sessions : aggregateStats . total_sessions || 0
354+ }
355+ } ) ;
356+ } catch ( error ) {
357+ console . error ( '[LOG statistics] ========= Error fetching study sessions:' , error ) ;
358+ res . status ( 500 ) . json ( {
359+ message : 'Error fetching study sessions' ,
360+ error : error . message
361+ } ) ;
362+ }
363+ } ;
364+
165365/**
166366 * Update topic progress
167367 */
@@ -461,6 +661,9 @@ const logStudySession = async (req, res) => {
461661
462662module . exports = {
463663 getUserStatistics,
664+ getQuizPerformance,
665+ getNoteProgress,
666+ getStudySessions,
464667 updateTopicProgress,
465668 updateQuizProgress,
466669 updateNoteProgress,
0 commit comments