diff --git a/owl/home.py b/owl/home.py index 7b7cdaa084acf4d68c3ef7ad6d3cb0bf689533bb..1654665fd87e81a3495e451224bab19c7fb92d1e 100644 --- a/owl/home.py +++ b/owl/home.py @@ -2,7 +2,7 @@ from flask import g, request, url_for, render_template, redirect, session, flash from flask_wtf import FlaskForm -from sqlalchemy import select, update, and_, or_ +from sqlalchemy import select, update, and_, or_, union, literal from sqlalchemy.orm import joinedload import sqlalchemy.sql.functions as func from typing import Dict, List, Tuple @@ -21,34 +21,61 @@ def index(): sess = db.get_session() semesters, enrolls, enrolls_by_sem = list_courses(True) - # For now, we calculate new post statictics only for teachers. - # It would be nice to extend it to students when we verify that - # the performance is reasonable. For students, we will need to - # limit target uids to g.uid and -1 (and probably mix these two - # together). - course_ids = set(e.course.cid for e in enrolls if e.is_teacher) + course_ids_teacher = set(e.course.cid for e in enrolls if e.is_teacher) + course_ids_student = set(e.course.cid for e in enrolls if not e.is_teacher) + course_ids = course_ids_teacher | course_ids_student if course_ids: - mtime_subq = ( - select(db.Topic.cid, db.Post.tid, db.Post.target_uid, func.max(db.Post.created).label('last_created'), func.max(db.Post.modified).label('last_modified')) - .select_from(db.Post) - .join(db.Topic) - .filter(db.Topic.cid.in_(course_ids)) - .filter(db.Post.target_uid >= 0) - .filter(db.Post.author_uid >= 0) - .group_by(db.Topic.cid, db.Post.tid, db.Post.target_uid) - .subquery() - ) - - seen_subq = ( - select(db.Seen.tid, db.Seen.target_uid, func.max(db.Seen.seen).label('last_seen')) - .select_from(db.Seen) - .join(db.Topic, db.Topic.tid == db.Seen.tid) - .join(db.Enroll, and_(db.Enroll.cid == db.Topic.cid, db.Enroll.uid == db.Seen.observer_uid)) - .filter(db.Enroll.cid.in_(course_ids)) - .filter(db.Enroll.is_teacher == True) - .group_by(db.Seen.tid, db.Seen.target_uid) - .subquery() - ) + mtime_subqs = [] + seen_subqs = [] + + if course_ids_teacher: + # Consider non-global posts in all threads of the course + mtime_subqs.append( + select(db.Topic.cid, db.Post.tid, db.Post.target_uid, func.max(db.Post.created).label('last_created'), func.max(db.Post.modified).label('last_modified')) + .select_from(db.Post) + .join(db.Topic) + .filter(db.Topic.cid.in_(course_ids_teacher)) + .filter(db.Post.author_uid >= 0) + .filter(db.Post.target_uid >= 0) + .group_by(db.Topic.cid, db.Post.tid, db.Post.target_uid) + ) + + # Consider acknowledges by all teachers of the course + seen_subqs.append( + select(db.Seen.tid, db.Seen.target_uid, func.max(db.Seen.seen).label('last_seen')) + .select_from(db.Seen) + .join(db.Topic, db.Topic.tid == db.Seen.tid) + .join(db.Enroll, and_(db.Enroll.cid == db.Topic.cid, db.Enroll.uid == db.Seen.observer_uid)) + .filter(db.Enroll.cid.in_(course_ids_teacher)) + .filter(db.Enroll.is_teacher == True) + .group_by(db.Seen.tid, db.Seen.target_uid) + ) + + if course_ids_student: + # Consider only global posts and posts in my threads, all in public topics only + mtime_subqs.append( + select(db.Topic.cid, db.Post.tid, literal(g.uid).label('target_uid'), func.max(db.Post.created).label('last_created'), func.max(db.Post.modified).label('last_modified')) + .select_from(db.Post) + .join(db.Topic) + .filter(db.Topic.cid.in_(course_ids_student)) + .filter(db.Topic.public == True) + .filter(db.Post.author_uid >= 0) + .filter(db.Post.target_uid.in_((g.uid, -1))) + .group_by(db.Topic.cid, db.Post.tid) + ) + + # Consider only my acknowledges + seen_subqs.append( + select(db.Seen.tid, literal(g.uid).label('target_uid'), func.max(db.Seen.seen).label('last_seen')) + .select_from(db.Seen) + .join(db.Topic, db.Topic.tid == db.Seen.tid) + .filter(db.Seen.observer_uid == g.uid) + .filter(db.Topic.cid.in_(course_ids_student)) + .group_by(db.Seen.tid) + ) + + mtime_subq = union(*mtime_subqs).subquery() + seen_subq = union(*seen_subqs).subquery() new_post_counts = sess.execute( select(mtime_subq.c.cid, func.count())