diff --git a/db.py b/db.py index 55484b9e831550c75331bff91e1445236505ea46..3a2d9f593625ff8bc19913060e56e10645635bd3 100644 --- a/db.py +++ b/db.py @@ -25,7 +25,7 @@ def dict_factory(cursor, row): return d # From sqlite3 module, but with error catching -def sqlite_convert_timestamp(val): +def convert_timestamp(val): try: datepart, timepart = val.split(b" ") year, month, day = map(int, datepart.split(b"-")) @@ -36,12 +36,12 @@ def sqlite_convert_timestamp(val): else: microseconds = 0 val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds) - except: + except ValueError: val = None return val -sqlite3.register_converter('datetime', sqlite_convert_timestamp) -sqlite3.register_converter('timestamp', sqlite_convert_timestamp) +sqlite3.register_converter('datetime', convert_timestamp) +sqlite3.register_converter('timestamp', convert_timestamp) def query(operation, *params): if config['DB_ENGINE'] == 'mysql': diff --git a/feeds.py b/feeds.py new file mode 100644 index 0000000000000000000000000000000000000000..e44ca6101f5356b0719d8d5ef683e4589b96d394 --- /dev/null +++ b/feeds.py @@ -0,0 +1,55 @@ +from server import * + +def gen_atomid(s): + return 'urn:md5:'+hashlib.md5(s.encode('utf-8')).hexdigest().upper() + +def fixdate(d): + if not isinstance(d, datetime): + return datetime(MINYEAR, 1, 1) + return d + +@app.route('/feed') +@app.route('/course/<handle>/feed') +@handle_errors(None, 'Diese Veranstaltung existiert nicht!', 400, IndexError) +def feed(handle=None): + id = None + course = {'id': None, 'title': 'Neueste Videos', 'time_created': None, 'time_updated': None} + course['atomid'] = gen_atomid('FROM videos SELECT *') + if handle: + course = query('SELECT * FROM courses WHERE handle = ? AND visible', handle)[0] + course['atomid'] = gen_atomid('Video AG, courses['+str(course['id'])+']: '+course['handle']) + id = course['id'] + entries = query(''' + SELECT lectures.*, videos.file_size, videos.path, videos.id AS video_id, videos.hash, + MAX(videos.time_created, videos.time_updated, lectures.time_created, lectures.time_updated) as updated, + courses.title AS course_title, courses.handle AS course_handle, courses.semester, courses.organizer AS course_organizer, courses.short as course_short, + formats.description AS format_description, formats.player_prio + FROM lectures + JOIN courses ON (courses.id = lectures.course_id) + JOIN videos ON (lectures.id = videos.lecture_id) + JOIN formats ON (formats.id = videos.video_format) + WHERE (? IS NULL) OR course_id = ? AND courses.visible AND lectures.visible AND videos.visible + ORDER BY time DESC, player_prio + LIMIT 100''', + course['id'], course['id']) + updated = max(course['time_updated'], course['time_created'], key=fixdate) + for entry in entries: + entry['updated'] = convert_timestamp(entry['updated'].encode('utf-8')) + if len(entry['hash']) != 32: + entry['atomid'] = gen_atomid('Video AG, videos['+str(entry['video_id'])+']') + else: + entry['atomid'] = 'urn:md5:'+(entry['hash'].upper()) + updated = max(updated, entry['updated'], key=fixdate) + course['updated'] = updated + return Response(render_template('feed.atom', course=course, entries=entries), 200, {'Content-Type': 'application/atom+xml'}) + +@app.route('/course/feed') +def feed_courses(): + courses = query('SELECT *, MAX(time_created, time_updated) AS updated FROM courses WHERE visible AND listed ORDER BY time_created DESC LIMIT 100') + atomid = gen_atomid('Video AG, courses') + updated = None + for course in courses: + course['updated'] = convert_timestamp(course['updated'].encode('utf-8')) + course['atomid'] = gen_atomid('Video AG, courses['+str(course['id'])+']: '+course['handle']) + updated = max(updated, course['updated'], key=fixdate) + return Response(render_template('feed_courses.atom', updated=updated, atomid=atomid, courses=courses), 200, {'Content-Type': 'application/atom+xml'}) diff --git a/server.py b/server.py index 8a5f92aced2c51020baa99e66bc61304e938fd9d..f044766216585f5c3e46965f7a0c5513cfa9117f 100755 --- a/server.py +++ b/server.py @@ -1,9 +1,10 @@ from flask import Flask, g, request, url_for, redirect, session, render_template, flash, Response from werkzeug.routing import Rule from functools import wraps -from datetime import date, timedelta, datetime, time +from datetime import date, timedelta, datetime, time, MINYEAR import threading import os +import hashlib app = Flask(__name__) @@ -36,7 +37,7 @@ config.from_pyfile('config.py', silent=True) if config['DEBUG']: app.jinja_env.auto_reload = True -from db import query, searchquery, ldapauth, ldapget +from db import query, searchquery, ldapauth, ldapget, convert_timestamp mod_endpoints = [] @@ -78,7 +79,10 @@ def handle_errors(endpoint, text, code, *errors, **epargs): try: return func(*args, **kwargs) except errors: - return render_endpoint(endpoint, text, **epargs), code + if endpoint: + return render_endpoint(endpoint, text, **epargs), code + else: + return text, code return decorator return wrapper @@ -86,6 +90,18 @@ def handle_errors(endpoint, text, code, *errors, **epargs): def handle_not_found(e): return render_endpoint('index', 'Diese Seite existiert nicht!'), 404 +@app.template_filter(name='semester') +def human_semester(s): + return s + +@app.template_filter(name='date') +def human_date(d): + return d.strftime('%x') + +@app.template_filter() +def rfc3339(d): + return d.strftime('%Y-%m-%dT%H:%M:%S+02:00') + @app.route('/') @register_navbar('Home', icon='home') def index(): @@ -212,7 +228,6 @@ def edit(): query('COMMIT') return "OK", 200 - @app.route('/auth') def auth(): # For use with nginx auth_request if 'X-Original-Uri' not in request.headers: @@ -346,3 +361,9 @@ def import_from(numid=None, source=None, id=None): courses = query('SELECT * FROM courses WHERE handle = ?', id)[0] lectures = query('SELECT * FROM lectures WHERE course_id = ?', courses['id']) return render_template('import_campus.html', course=courses, lectures=lectures, campus=campus) + +@app.route('/files/<filename>') +def files(filename): + return redirect(config['VIDEOPREFIX']+'/'+filename) + +import feeds