diff --git a/cutprogress.py b/cutprogress.py index e08b9b5ac19d466e5af28b5399f42c3b574cca44..1d8a238674ab8be87ef6957564592d696d515035 100644 --- a/cutprogress.py +++ b/cutprogress.py @@ -40,15 +40,21 @@ def cutprogress(user=None): lectures.course_id, lectures.time, lectures.title, - COUNT(videos.id) as videos_total, - COUNT(videos.visible) as videos_visible + video_counts.videos_total, + video_counts.videos_visible FROM lectures - JOIN courses ON courses.id = lectures.course_id - LEFT JOIN videos ON lectures.id = videos.lecture_id + JOIN courses ON ( courses.id = lectures.course_id ) + LEFT JOIN ( + SELECT + videos.lecture_id, + COUNT(videos.id) as videos_total, + COUNT(videos.visible) as videos_visible + FROM videos + GROUP BY videos.lecture_id + ) AS video_counts ON ( video_counts.lecture_id = lectures.id ) WHERE courses.id = ? AND lectures.time <= ? AND NOT lectures.norecording - GROUP BY lectures.id ORDER BY lectures.time ASC, lectures.id ASC ''', course['id'], datetime.now()) # Generate list of days, figure out when weeks change diff --git a/db.py b/db.py index 583035c9336b4f33e2406395c8fddd833da3987d..2095b679ec6a640e7468ddf528ceb3445650e1ba 100644 --- a/db.py +++ b/db.py @@ -148,20 +148,3 @@ def close_db(*args): #pylint: disable=unused-argument if 'db' in g: g.db.close() del g.db - -def searchquery(text, columns, match, tables, suffix, *suffixparams): - params = [] - subexprs = [] - words = text.split(' ') - prio = len(words)+1 - for word in words: - if word == '' or word.isspace(): - continue - matchexpr = ' OR '.join(['%s LIKE ?'%column for column in match]) - subexprs.append('SELECT %s, %s AS _prio FROM %s WHERE %s'%(columns, str(prio), tables, matchexpr)) - params += ['%'+word+'%']*len(match) - prio -= 1 - if subexprs == []: - return [] - expr = 'SELECT *,SUM(_prio) AS _score FROM (%s) AS _tmp %s'%(' UNION '.join(subexprs), suffix) - return query(expr, *(list(params)+list(suffixparams))) diff --git a/edit.py b/edit.py index 6da7d39ff231798cd1b5512a8ec173020984dbc4..085a4b403e5455ad907f7831c21eb78c7e7b463e 100644 --- a/edit.py +++ b/edit.py @@ -255,7 +255,8 @@ def changelog(): @csrf_protect def set_responsible(course_id, user_id, value): if value: - modify('REPLACE INTO responsible (course_id, user_id) values (?, ?)', course_id, user_id) + if not query('SELECT id FROM responsible WHERE course_id = ? AND user_id = ?', course_id, user_id): + modify('INSERT INTO responsible (course_id, user_id) VALUES (?, ?)', course_id, user_id) else: modify('DELETE FROM responsible WHERE course_id = ? AND user_id = ?', course_id, user_id) return "OK", 200 diff --git a/jobs.py b/jobs.py index 13f64ff05453e72596d70db106cedeaeddc6b801..e32bbe7c0231eb70970590f7417798ae783316e1 100644 --- a/jobs.py +++ b/jobs.py @@ -87,7 +87,10 @@ def jobs_ping(id): @app.route('/internal/jobs/api/worker/<hostname>/schedule', methods=['POST']) @api_token_required('JOBS_API_KEY') def jobs_schedule(hostname): - query('REPLACE INTO worker (hostname, last_ping) values (?, ?)', hostname, datetime.now()) + if query("SELECT hostname FROM worker WHERE hostname = ?", hostname): + query("UPDATE worker SET last_ping = ? WHERE hostname = ?", datetime.now(), hostname) + else: + query("INSERT INTO worker (hostname, last_ping) VALUES (?, ?)", hostname, datetime.now()) hostdata = request.get_json() if not hostdata: return 'no hostdata sent', 400 diff --git a/livestreams.py b/livestreams.py index b7e7cc3eb386b8a7a62f3ccac3ea8d94fbba83a6..dcb0e3070ef9c4decdf0baa19ca7edde76a02050 100644 --- a/livestreams.py +++ b/livestreams.py @@ -44,10 +44,8 @@ def streamauth_legacy(server=None): break if 'lecture' in request.values: match = {'id': request.values['lecture']} - try: + if not query("SELECT handle FROM streams WHERE handle = ?", request.values['name']): modify("INSERT INTO streams (handle, active, visible, lecture_id, description, poster) VALUES (?, false, true, -1, '', '')", request.values['name']) - except: - pass if server: data = {'src': 'rtmp://%s/live/%s'%(server, request.values['name']), 'destbase': 'rtmp://%s/hls/%s'%(server, request.values['name'])} diff --git a/server.py b/server.py index ab43eb8edf265404e46f930b662639f60f6efe63..845fa30f9a85ce6a353ead5b58c36cb5eb8136e4 100644 --- a/server.py +++ b/server.py @@ -71,7 +71,7 @@ def evalperm(perms): return [{'type': 'public'}] #pylint: disable=wrong-import-position -from db import query, modify, show, searchquery +from db import query, modify, show from template_helper import * from mail import notify_mods, notify_admins #pylint: disable=unused-import from ldap import ldapauth @@ -171,13 +171,21 @@ def index(): i['date'] = i['time'].date() latestvideos = query(''' SELECT lectures.*, \'course\' AS sep, courses.* - FROM lectures - LEFT JOIN videos ON (videos.lecture_id = lectures.id) - LEFT JOIN courses on (courses.id = lectures.course_id) - WHERE (? OR (courses.visible AND courses.listed AND lectures.visible AND videos.visible)) - GROUP BY videos.lecture_id - ORDER BY MAX(videos.time_created) DESC - LIMIT 6 ''', ismod()) + FROM ( + SELECT + videos.lecture_id, + MAX(videos.time_created) AS _time_publish + FROM videos + JOIN lectures ON ( lectures.id = videos.lecture_id ) + JOIN courses ON ( courses.id = lectures.course_id ) + WHERE (? OR (courses.visible AND courses.listed AND lectures.visible AND videos.visible)) + GROUP BY videos.lecture_id + ORDER BY _time_publish DESC + LIMIT 6 + ) AS _latest + JOIN lectures ON ( lectures.id = _latest.lecture_id ) + JOIN courses ON ( courses.id = lectures.course_id ) + ''', ismod()) livestreams = query('''SELECT streams.handle AS livehandle, lectures.*, \'course\' AS sep, courses.* FROM streams JOIN lectures ON lectures.id = streams.lecture_id @@ -247,7 +255,7 @@ def course(id=None, handle=None): for i in query('SELECT lectures.id AS id, COUNT(chapters.id) AS c FROM chapters \ JOIN lectures ON chapters.lecture_id = lectures.id \ WHERE lectures.course_id = ? AND NOT chapters.visible AND NOT chapters.deleted \ - GROUP BY chapters.lecture_id;', course['id']): + GROUP BY lectures.id;', course['id']): chapters[i['id']] = i['c'] lectures = query('SELECT * FROM lectures WHERE course_id = ? AND (? OR visible) ORDER BY time, duration DESC', course['id'], ismod()) for lecture in lectures: @@ -395,31 +403,8 @@ def search(): if 'q' not in request.args: return redirect(url_for('index')) searchtext = request.args['q'] - courses = searchquery(searchtext, '*', ['title', 'short', 'organizer', 'subject', 'description'], - 'courses', 'WHERE (? OR (visible AND listed)) GROUP BY id ORDER BY _score DESC, semester DESC LIMIT 20', ismod()) - lectures = searchquery(searchtext, 'lectures.*, \ - courses.visible AS coursevisible, \ - courses.listed, \ - courses.id AS courses_id, \ - courses.visible AS courses_visible, \ - courses.listed AS courses_listed, \ - courses.title AS courses_title, \ - courses.short AS courses_short, \ - courses.handle AS courses_handle, \ - courses.organizer AS courses_organizer, \ - courses.subject AS courses_subject, \ - courses.credits AS courses_credits, \ - courses.created_by AS courses_created_by, \ - courses.time_created AS courses_time_created, \ - courses.time_updated AS courses_time_updated, \ - courses.semester AS courses_semester, \ - courses.downloadable AS courses_downloadable, \ - courses.embedinvisible AS courses_embedinvisible, \ - courses.description AS courses_description, \ - courses.internal AS courses_internal', - ['lectures.title', 'lectures.comment', 'lectures.speaker', 'courses.short'], - 'lectures LEFT JOIN courses on (courses.id = lectures.course_id)', - 'WHERE (? OR (coursevisible AND listed AND visible)) GROUP BY id ORDER BY _score DESC, time DESC LIMIT 30', ismod()) + courses = _course_query_search(searchtext, ismod()) + lectures = _lecture_query_search(searchtext, ismod()) for lecture in lectures: lecture['course'] = {} for key in lecture: @@ -427,6 +412,99 @@ def search(): lecture['course'][key[8:]] = lecture[key] return render_template('search.html', searchtext=searchtext, courses=courses, lectures=lectures) + +# This search is basically stolen from the new api + +def _course_query_search(search_term: str, is_mod: bool): + return _query_search( + "courses", + ["title", "short", "organizer", "subject", "description"], + None, + None, + None if is_mod else 'WHERE "courses"."visible" AND "courses"."listed"', + '"courses"."semester" DESC', + 20, + search_term + ) + + +def _lecture_query_search(search_term: str, is_mod: bool): + return _query_search( + "lectures", + ["title", "comment", "speaker"], + 'JOIN "courses" ON ("lectures"."course_id" = "courses"."id")', + """\ +courses.visible AS coursevisible, \ +courses.listed, \ +courses.id AS courses_id, \ +courses.visible AS courses_visible, \ +courses.listed AS courses_listed, \ +courses.title AS courses_title, \ +courses.short AS courses_short, \ +courses.handle AS courses_handle, \ +courses.organizer AS courses_organizer, \ +courses.subject AS courses_subject, \ +courses.credits AS courses_credits, \ +courses.created_by AS courses_created_by, \ +courses.time_created AS courses_time_created, \ +courses.time_updated AS courses_time_updated, \ +courses.semester AS courses_semester, \ +courses.downloadable AS courses_downloadable, \ +courses.embedinvisible AS courses_embedinvisible, \ +courses.description AS courses_description, \ +courses.internal AS courses_internal +""", + None if is_mod else 'WHERE "courses"."visible" AND "courses"."listed" AND "lectures"."visible"', + '"lectures"."time" DESC', + 30, + search_term + ) + + +def _query_search( + table: str, + search_columns: list[str], + join_clause: str or None, + extra_select_columns: str or None, + where_clause: str or None, + extra_ordering: str or None, + limit: int, + search_term: str): + base_sub_query = f""" + SELECT "{table}"."id" AS "_id", CAST(%s AS INT) AS "_priority" FROM "{table}" WHERE {" OR ".join( + map(lambda column: f'LOWER("{table}"."{column}") LIKE ?', + search_columns))} + """ + + words: list[str] = list(filter(lambda w: not w.isspace(), search_term.split(" "))) + if len(words) == 0: + return [] + sub_queries: list[str] = [] + all_values: list[DbValueType] = [] + prio = len(words) + for word in words: + word = word.lower() + word = word.replace("%", "\\%").replace("_", "\\_") + word = "%" + word + "%" + sub_queries.append(base_sub_query % prio) + for _ in range(0, len(search_columns)): + all_values.append(word) + prio -= 1 + + return query(f""" + SELECT "{table}".* {"" if extra_select_columns is None else "," + extra_select_columns} + FROM "{table}" + JOIN ( + SELECT "_id", CAST(SUM("_priority") AS INT) AS "_score" + FROM ({"UNION ALL".join(sub_queries)}) AS "_sub_result" + GROUP BY "_id" + ) AS "_data" ON ("{table}"."id" = "_data"."_id") + {"" if join_clause is None else join_clause} + {"" if where_clause is None else where_clause} + ORDER BY "_data"."_score" DESC{"" if extra_ordering is None else ", " + extra_ordering} LIMIT {limit} + """, *all_values) + + def check_mod(user, groups): if not user: return False