diff --git a/zeeguu/api/endpoints/session_history.py b/zeeguu/api/endpoints/session_history.py index 5f5cd8086..c4f5ece30 100644 --- a/zeeguu/api/endpoints/session_history.py +++ b/zeeguu/api/endpoints/session_history.py @@ -134,17 +134,17 @@ def _count_interruptions(user_id, start_time, end_time, event_type): return count -def _calculate_focus_level(interruptions, duration_ms, word_count): +def _calculate_focus_level(interruptions, duration_sec, word_count): """ Calculate a focus level based on interruptions, duration, and engagement. Returns: 'focused', 'moderate', or 'distracted' """ - if duration_ms is None or duration_ms == 0: + if duration_sec is None or duration_sec == 0: return None # Calculate words per minute as engagement metric - duration_min = duration_ms / 60000 + duration_min = duration_sec / 60 words_per_min = word_count / duration_min if duration_min > 0 else 0 # Interruptions per 10 minutes @@ -207,14 +207,13 @@ def session_history(): sessions = [] - def format_duration(duration_ms): + def format_duration(duration_sec): """Format duration showing seconds for short durations, minutes for longer ones.""" - if duration_ms is None or duration_ms == 0: + if duration_sec is None or duration_sec == 0: return "0 sec" - seconds = duration_ms / 1000 - if seconds < 60: - return f"{int(seconds)} sec" - minutes = seconds / 60 + if duration_sec < 60: + return f"{int(duration_sec)} sec" + minutes = duration_sec / 60 if minutes < 60: return f"{round(minutes, 1)} min" hours = minutes / 60 @@ -290,12 +289,13 @@ def format_duration(duration_ms): for bs in browsing_sessions: bookmarks = _bookmarks_for_browsing_session(bs.id, learned_language.id) if bookmarks: # Only include browsing sessions with translations in learned language + duration_sec = (bs.duration or 0) // 1000 # stored in ms, normalize to seconds sessions.append( { "session_type": "browsing", "start_time": bs.start_time.isoformat(), - "duration": bs.duration, - "duration_readable": format_duration(bs.duration), + "duration": duration_sec, + "duration_readable": format_duration(duration_sec), "words": bookmarks, "word_count": len(bookmarks), } @@ -319,12 +319,13 @@ def format_duration(duration_ms): else: words = [] + duration_sec = (ls.duration or 0) // 1000 # stored in ms, normalize to seconds sessions.append( { "session_type": "audio", "start_time": ls.start_time.isoformat(), - "duration": ls.duration, # Already in milliseconds - "duration_readable": format_duration(ls.duration), + "duration": duration_sec, + "duration_readable": format_duration(duration_sec), "words": words, "word_count": len(words), "completed": is_completed, diff --git a/zeeguu/api/endpoints/user_stats.py b/zeeguu/api/endpoints/user_stats.py index 114664765..1d9e2939b 100644 --- a/zeeguu/api/endpoints/user_stats.py +++ b/zeeguu/api/endpoints/user_stats.py @@ -123,7 +123,7 @@ def get_exercise_stats_for_user(user_id, start, end): .all() ) - total_duration_ms = sum(s.duration or 0 for s in sessions) + total_duration_sec = sum(s.duration or 0 for s in sessions) session_count = len(sessions) # Get unique words practiced and language info @@ -139,8 +139,8 @@ def get_exercise_stats_for_user(user_id, start, end): return { "session_count": session_count, - "duration_ms": total_duration_ms, - "duration_min": round(total_duration_ms / 60000, 1), + "duration_sec": total_duration_sec, + "duration_min": round(total_duration_sec / 60, 1), "words_by_language": { lang: len(words) for lang, words in words_by_language.items() }, @@ -157,7 +157,7 @@ def get_reading_stats_for_user(user_id, start, end): .all() ) - total_duration_ms = sum(s.duration or 0 for s in sessions) + total_duration_sec = sum(s.duration or 0 for s in sessions) # Group by article and language articles_by_language = defaultdict(set) @@ -169,8 +169,8 @@ def get_reading_stats_for_user(user_id, start, end): return { "session_count": len(sessions), - "duration_ms": total_duration_ms, - "duration_min": round(total_duration_ms / 60000, 1), + "duration_sec": total_duration_sec, + "duration_min": round(total_duration_sec / 60, 1), "articles_by_language": { lang: len(arts) for lang, arts in articles_by_language.items() }, @@ -506,7 +506,7 @@ def user_stats_individual(user_id): { "id": session.id, "start_time": session.start_time.isoformat(), - "duration_min": round((session.duration or 0) / 60000, 1), + "duration_min": round((session.duration or 0) / 60, 1), "language": session_lang, "word_count": len(words), "words": words, @@ -529,7 +529,7 @@ def user_stats_individual(user_id): { "id": session.id, "start_time": session.start_time.isoformat(), - "duration_min": round((session.duration or 0) / 60000, 1), + "duration_min": round((session.duration or 0) / 60, 1), "article_id": session.article_id, "article_title": article.title if article else "Unknown", "article_language": ( @@ -949,8 +949,8 @@ def user_stats_individual_dashboard(user_id): ) # Calculate totals - total_exercise_min = sum((s.duration or 0) / 60000 for s in exercise_sessions) - total_reading_min = sum((s.duration or 0) / 60000 for s in reading_sessions) + total_exercise_min = sum((s.duration or 0) / 60 for s in exercise_sessions) + total_reading_min = sum((s.duration or 0) / 60 for s in reading_sessions) total_audio_min = sum((l.duration_seconds or 0) / 60 for l in audio_lessons) html = f""" @@ -1078,7 +1078,7 @@ def user_stats_individual_dashboard(user_id): if exercise_sessions: for session in exercise_sessions: exercises = Exercise.query.filter(Exercise.session_id == session.id).all() - duration_min = (session.duration or 0) / 60000 + duration_min = (session.duration or 0) / 60 # Determine language from exercises session_lang = None @@ -1126,7 +1126,7 @@ def user_stats_individual_dashboard(user_id): if reading_sessions: for session in reading_sessions: - duration_min = (session.duration or 0) / 60000 + duration_min = (session.duration or 0) / 60 article = session.article article_title = article.title if article else "Unknown article" article_lang = ( @@ -2245,7 +2245,7 @@ def _compute_activity_stats_for_month(month_start, month_end): from sqlalchemy import func # Exercise minutes - exercise_ms = ( + exercise_sec = ( db_session.query(func.sum(UserExerciseSession.duration)) .filter(UserExerciseSession.start_time >= month_start) .filter(UserExerciseSession.start_time < month_end) @@ -2253,7 +2253,7 @@ def _compute_activity_stats_for_month(month_start, month_end): ) or 0 # Reading minutes - reading_ms = ( + reading_sec = ( db_session.query(func.sum(UserReadingSession.duration)) .filter(UserReadingSession.start_time >= month_start) .filter(UserReadingSession.start_time < month_end) @@ -2277,8 +2277,8 @@ def _compute_activity_stats_for_month(month_start, month_end): ) or 0 return { - "exercise_minutes": round(exercise_ms / 60000), - "reading_minutes": round(reading_ms / 60000), + "exercise_minutes": round(exercise_sec / 60), + "reading_minutes": round(reading_sec / 60), "browsing_minutes": round(browsing_ms / 60000), "audio_minutes": round(audio_sec / 60), } diff --git a/zeeguu/core/model/user_exercise_session.py b/zeeguu/core/model/user_exercise_session.py index 76cb733ce..d1d8cf261 100644 --- a/zeeguu/core/model/user_exercise_session.py +++ b/zeeguu/core/model/user_exercise_session.py @@ -26,7 +26,7 @@ class UserExerciseSession(db.Model): user = db.relationship(User) start_time = db.Column(db.DateTime) - duration = db.Column(db.Integer) # Duration time in miliseconds + duration = db.Column(db.Integer) # Duration time in seconds last_action_time = db.Column(db.DateTime) is_active = db.Column(db.Boolean) @@ -46,7 +46,7 @@ def __init__(self, user_id, start_time, current_time=None, platform=None): self.last_action_time = current_time duration = self.last_action_time - self.start_time - self.duration = duration.total_seconds() * 1000 + self.duration = duration.total_seconds() def exercises_in_session_string(self): from zeeguu.core.sql.learner.exercises_history import exercises_in_session diff --git a/zeeguu/core/model/user_reading_session.py b/zeeguu/core/model/user_reading_session.py index 60d924ab2..0954e4933 100644 --- a/zeeguu/core/model/user_reading_session.py +++ b/zeeguu/core/model/user_reading_session.py @@ -30,7 +30,7 @@ class UserReadingSession(db.Model): article = db.relationship(Article) start_time = db.Column(db.DateTime) - duration = db.Column(db.Integer) # Duration time in miliseconds + duration = db.Column(db.Integer) # Duration time in seconds last_action_time = db.Column(db.DateTime) is_active = db.Column(db.Boolean) @@ -54,7 +54,7 @@ def __init__(self, user_id, article_id, current_time=None, reading_source=None, self.platform = platform def human_readable_duration(self): - return human_readable_duration(self.duration) + return str(round(self.duration / 60, 1)) + "min" def human_readable_date(self): return human_readable_date(self.start_time) diff --git a/zeeguu/core/reading_analysis/macro_reading_session.py b/zeeguu/core/reading_analysis/macro_reading_session.py index 61b594585..f8ce1c05d 100644 --- a/zeeguu/core/reading_analysis/macro_reading_session.py +++ b/zeeguu/core/reading_analysis/macro_reading_session.py @@ -26,7 +26,7 @@ def __init__(self, user_article): def append(self, session): self.sessions.append(session) - self.total_time += session.duration / 1000 + self.total_time += session.duration self.reading_speed = int(self.article.word_count * 60 / self.total_time) def start_date(self): @@ -49,7 +49,7 @@ def print_details(self): print("\tSessions: ") for session in self.sessions: - print(f"\t{session.start_time}, {session.duration / 1000}s") + print(f"\t{session.start_time}, {session.duration}s") print(" ") def print_summary(self): @@ -76,7 +76,7 @@ def macro_sessions_for_user(user, language_id): if not user_article: continue - if session.duration < 1000: + if session.duration < 1: # less than 1s is not a session continue diff --git a/zeeguu/core/user_statistics/activity.py b/zeeguu/core/user_statistics/activity.py index 657246751..89443a233 100644 --- a/zeeguu/core/user_statistics/activity.py +++ b/zeeguu/core/user_statistics/activity.py @@ -56,7 +56,7 @@ def _time_by_day(user, table_name, date_field, duration_field): # Safe to use string formatting here since values are validated against whitelist query = ( f" SELECT date({date_field}) as date, " - + f" SUM({duration_field}) / 1000 as duration " + + f" SUM({duration_field}) as duration " + f" FROM {table_name}" + " WHERE user_id = :uid GROUP BY date;" )