Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions zeeguu/api/endpoints/session_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
}
Expand All @@ -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,
Expand Down
32 changes: 16 additions & 16 deletions zeeguu/api/endpoints/user_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
},
Expand All @@ -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)
Expand All @@ -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()
},
Expand Down Expand Up @@ -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,
Expand All @@ -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": (
Expand Down Expand Up @@ -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"""<!DOCTYPE html>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 = (
Expand Down Expand Up @@ -2245,15 +2245,15 @@ 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)
.scalar()
) 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)
Expand All @@ -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),
}
Expand Down
4 changes: 2 additions & 2 deletions zeeguu/core/model/user_exercise_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions zeeguu/core/model/user_reading_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions zeeguu/core/reading_analysis/macro_reading_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion zeeguu/core/user_statistics/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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;"
)
Expand Down
Loading