diff --git a/db_schema.sql b/db_schema.sql index 17034efec052294815572a4a48fc2719a7035de4..65b7cf8c2c5199133c70371f2dac65d56ea66c6f 100644 --- a/db_schema.sql +++ b/db_schema.sql @@ -194,8 +194,8 @@ CREATE TABLE IF NOT EXISTS `announcements` ( `level` INTEGER NOT NULL DEFAULT 0, `visible` INTEGER NOT NULL DEFAULT 0, `deleted` INTEGER NOT NULL DEFAULT 0, - `time_publish` datetime DEFAULT "", - `time_expire` datetime DEFAULT "", + `time_publish` datetime DEFAULT '', + `time_expire` datetime DEFAULT '', `time_created` datetime NOT NULL, `time_updated` datetime NOT NULL, `created_by` INTEGER NOT NULL @@ -203,8 +203,8 @@ CREATE TABLE IF NOT EXISTS `announcements` ( CREATE TABLE IF NOT EXISTS `featured` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `title` text NOT NULL DEFAULT '', - `text` text NOT NULL DEFAULT "", - `internal` text NOT NULL DEFAULT "", + `text` text NOT NULL DEFAULT '', + `internal` text NOT NULL DEFAULT '', `visible` INTEGER NOT NULL DEFAULT 0, `deleted` INTEGER NOT NULL DEFAULT 0, `time_created` datetime NOT NULL, @@ -219,8 +219,20 @@ CREATE TABLE IF NOT EXISTS `sortlog` ( `video_id` INTEGER NOT NULL ); +CREATE TABLE IF NOT EXISTS `sorterrorlog_data` ( +`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + `when` datetime NOT NULL, + `path` text NOT NULL, + `course_id` INTEGER NOT NULL, + `matches` text NOT NULL Default '', + `deleted` INTEGER NOT NULL DEFAULT '0', + `time_updated` datetime NOT NULL, + `time_created` datetime NOT NULL +); + CREATE VIEW IF NOT EXISTS `courses` AS select * from `courses_data` where (not(`courses_data`.`deleted`)); CREATE VIEW IF NOT EXISTS `lectures` AS select * from `lectures_data` where (not(`lectures_data`.`deleted`)); CREATE VIEW IF NOT EXISTS `videos` AS select * from `videos_data` where (not(`videos_data`.`deleted`)); CREATE VIEW IF NOT EXISTS `auth` AS select * from `auth_data` where (not(`auth_data`.`deleted`)); +CREATE VIEW IF NOT EXISTS `sorterrorlog` AS select * from `sorterrorlog_data` where (not(`sorterrorlog_data`.`deleted`)); COMMIT; diff --git a/server.py b/server.py index bd0c7691d2aa01e787c8b799269acabfd9a33585..13aea0a8e8c418b2c34e2ee992f9a41a09b867a3 100644 --- a/server.py +++ b/server.py @@ -276,6 +276,7 @@ def logout(): session.pop('user') return redirect(request.values.get('ref', url_for('index'))) +# name: (tablename, idcolumn, [editable_fields], [fields_to_set_at_creation_time]) tabs = { 'courses': ('courses_data', 'id', ['visible', 'listed', 'title', 'short', 'handle', 'organizer', 'subject', 'semester', 'downloadable', @@ -294,7 +295,9 @@ tabs = { 'featured': ('featured', 'id', ['title', 'text', 'internal', 'visible', 'deleted'], ['created_by', 'time_created', 'time_updated']), 'auth': ('auth_data', 'auth_id', ['auth_type', 'auth_user', 'auth_passwd', 'deleted'], - ['course_id', 'lecture_id', 'video_id', 'created_by', 'time_created', 'time_updated']) + ['course_id', 'lecture_id', 'video_id', 'created_by', 'time_created', 'time_updated']), + 'sorterrorlog': ('sorterrorlog_data', 'id', ['deleted'], + ['time_created', 'time_updated']) } @app.route('/edit', methods=['GET', 'POST']) diff --git a/sorter.py b/sorter.py index 9f45cd36f718f60fe584af25b68ed473367c02d0..af01c081c1eee2d5934ac23456297425a3ac5b65 100644 --- a/sorter.py +++ b/sorter.py @@ -1,4 +1,5 @@ from server import * +import traceback @app.route('/sort/log') @register_navbar('Sortierlog', icon='sort-by-attributes-alt') @@ -16,44 +17,54 @@ def sort_log(): JOIN courses ON courses.id = lectures.course_id ORDER BY sortlog.`when` DESC LIMIT 50 - ''')) - + '''),sorterrorlog=query('SELECT * FROM sorterrorlog ORDER BY sorterrorlog.`when` DESC')) +def to_ascii(inputstring): + asciistring = inputstring + for charset in [('ä', 'ae'), ('ö', 'oe'), ('ü', 'ue'), ('ß', 'ss')]: + asciistring = asciistring.replace(charset[0],charset[1]) + return asciistring @app.route('/sort/now') @mod_required @sched_func(600) def sort_now(): + modify('BEGIN') courses = query('SELECT * FROM courses') formats = query('SELECT * FROM formats ORDER BY prio') - for c in courses: + for course in courses: for mountpoint in config['VIDEOMOUNT']: - existingvideos = query('SELECT videos.path FROM videos JOIN lectures ON (videos.lecture_id = lectures.id) WHERE lectures.course_id = ?',c['id']) - lectures = query('SELECT * from lectures where course_id = ?',c['id']) - coursepath = mountpoint['mountpoint']+c['handle'] + existingvideos = query('SELECT videos.path FROM videos JOIN lectures ON (videos.lecture_id = lectures.id) WHERE lectures.course_id = ?',course['id']) + knownerrors = query('SELECT sorterrorlog.path FROM sorterrorlog WHERE sorterrorlog.course_id = ?',course['id']) + ignorefiles = existingvideos + knownerrors + lectures = query('SELECT * from lectures where course_id = ?',course['id']) + coursepath = mountpoint['mountpoint']+course['handle'] try: files = os.listdir(coursepath) except FileNotFoundError: files = [] - for f in files: + for filename in files: try: - # if the video is in the table "videos" already, skip it - exists = False - for e in existingvideos: + # if the video is in the table "videos" already (with the correct course), skip it + ignore = False + for file_to_ignore in ignorefiles: + # path is something like # vpnonline/08ws-swt/08ws-swt-081118.mp4 - e_filename = e['path'] - if os.path.basename(f) == os.path.basename(e_filename): - exists = True + if os.path.basename(filename) == os.path.basename(file_to_ignore['path']): + ignore = True break - if exists: + if ignore: continue - filepath = coursepath + '/' + f + filepath = coursepath + '/' + filename # filenames: <handle>-<sorter>-<format>.mp4 - # sorter musst be found with fuzzy matching. musst be one or more of the following: (inside the loop) - splitfilename = f.replace('_','-').replace(' ','-').split('-') - if not os.path.splitext(f)[1] == '.mp4': + # "sorter" musst be found with fuzzy matching. "sorter" musst be one or more of the following types: (inside the loop) + + # '_' and ' ' are handled like '-' + splitfilename = filename.replace('_','-').replace(' ','-').split('-') + if not os.path.splitext(filename)[1] == '.mp4': continue + # we save all extraced data in a dict data = {'keywords': []} # parse the file name and save all data in 'data' for s in splitfilename: @@ -61,7 +72,7 @@ def sort_now(): #-<YYMMDD> (date) #-<HHMM> (time) #-<keyword> - # Looking for keywords in: id,title,speaker,comment, comma seperated list in internal + # Looking for keywords in: title,speaker,comment, comma seperated list in internal try: if len(s) == 6: data['date'] = datetime.strptime(s,'%y%m%d').date() @@ -70,28 +81,35 @@ def sort_now(): else: data['keywords'].append(s) except ValueError: + # if its not a date or time, handle it as keyword data['keywords'].append(s) # try to match the file on a single lecture matches = [] # first try date and time (if one of them is set) - if 'date' in data: - for l in lectures: - if ((l['time'].date() == data['date']) and not ('time' in data)) or (('time' in data) and l['time'] == datetime.combine(data['date'],data['time'])) : - matches.append(l) - # if we can't match based on date and time, we have to match keywords + if ('date' in data) or ('time' in data): + for lecture in lectures: + if not ('time' in lecture) or not lecture['time']: + continue + if ('date' in data) and (lecture['time'].date() != data['date']): + continue + if ('time' in data) and (lecture['time'].time() != data['time']): + continue + matches.append(lecture) + # if we can't match exactly based on date and time, we have to match keywords if ((len(matches) != 1) and (len(data['keywords']) > 0)): + #only test lectures with the correct date/time, if we have any. Else test for matches in all lectures of this course + if len(matches) == 0: + matches.extend(lectures) found = False - for field in ['id','title','speaker','comment','internal']: - #only test lectures with the correct date/time - if len(matches) == 0: - matches.extend(lectures) - for l in matches: - for k in data['keywords']: + for field in ['title','speaker','comment','internal']: + for lecture in matches: + for keyword in data['keywords']: # first test for exact match, else make it asci and try substring test - if (k == l[field]) or (str(k).lower() in str(l[field]).lower().replace('ä','ae').replace('ü','ue').replace('ö','oe').replace('ß','ss') ): + if (keyword == lecture[field]) or \ + (str(keyword).lower() in str(to_ascii(lecture[field]).lower())): found = True - matches = [l] + matches = [lecture] if found: break if found: @@ -99,32 +117,37 @@ def sort_now(): if found: break # now we should have found exactly one match + dbfilepath = mountpoint['prefix']+course['handle']+'/'+filename if len(matches) == 1: # now match the format - for i in formats: + for videoformat in formats: #we match the last part of the file name without the extension formatstring = splitfilename[-1].split('.',1)[0].lower() - if formatstring in i['keywords'].replace(',',' ').split(' '): - data['format'] = i['id'] + if formatstring in videoformat['keywords'].replace(',',' ').split(' '): + data['format'] = videoformat['id'] break - # if we found the format, insert the video + # default format is "unknown", with id 0 if not 'format' in data: data['format'] = 0 - modify('BEGIN') - video_id = modify('INSERT INTO videos_data (lecture_id,visible,path,video_format,title,comment,internal,file_modified,time_created,time_updated,created_by,hash,file_size) VALUES (?,0,?,?,"","","",?,?,?,?,"",?)',matches[0]['id'],mountpoint['prefix']+c['handle']+'/'+f,data['format'],datetime.now(),datetime.now(),datetime.now(),-1,os.stat(coursepath+'/'+f).st_size) - query('INSERT INTO sortlog (lecture_id,video_id,path,`when`) VALUES (?,?,?,?)',matches[0]['id'],video_id,c['handle']+'/'+f,datetime.now()) - modify('COMMIT') -# for debuging only + # insert the video into videos_data and log + video_id = modify(''' + INSERT INTO videos_data + (lecture_id,visible,path,video_format,title,comment,internal,file_modified,time_created,time_updated,created_by,hash,file_size) + VALUES + (?,0,?,?,"","","",?,?,?,?,"",?)''', + matches[0]['id'], dbfilepath, data['format'], datetime.now(), datetime.now(), datetime.now(), -1, os.stat(filepath).st_size) + query('INSERT INTO sortlog (lecture_id,video_id,path,`when`) VALUES (?,?,?,?)', matches[0]['id'], video_id, dbfilepath, datetime.now()) else: - d = [] - for m in matches: - d.append(m['id']) - print('failed',{"data":data,"path":f,"results":d}) - except Exception as exc: -# raise exc - pass + # if we couldn't match the video on exactly one lecture, log an error + matches_id = [] + for match in matches: + matches_id.append(str(match['id'])) + query('INSERT INTO sorterrorlog_data (course_id,path,matches,`when`,time_updated,time_created) VALUES (?,?,?,?,?,?)', course['id'], dbfilepath, ','.join(matches_id), datetime.now(), datetime.now(), datetime.now()) + except Exception: + traceback.print_exc() + modify('COMMIT') if 'ref' in request.values: return redirect(request.values['ref']) else: diff --git a/templates/sortlog.html b/templates/sortlog.html index 6124fa96a05ac6e70b983b78218ba96e15bdfa58..184af50c9510d00fcd17941ceb9c187ddb532d66 100644 --- a/templates/sortlog.html +++ b/templates/sortlog.html @@ -1,14 +1,44 @@ -{% from 'macros.html' import preview %} +{% from 'macros.html' import moderator_delete %} {% extends "base.html" %} {% block content %} <div class="panel-group"> <div class="panel panel-default"> <div class="panel-heading"> - <h1 class="panel-title">Sortierlog <a class="btn btn-default" href="{{url_for('sort_now', ref=request.url)}}">Jetzt einsortieren</a></h1> + <h1 class="panel-title">Sortierlog + <a class="btn btn-default" href="{{url_for('sort_now', ref=request.url)}}">Jetzt einsortieren</a> + <button class="btn btn-default" onclick="$('button[data-path^=\'sorterrorlog.\'][data-path$=\'.deleted\']').each(function (e) { moderator.api.set($(this).data('path'),1,false); }); window.location.reload();">Alle Fehler entfernen</button> + </h1> </div> <div class="panel-body"> <p>Hier werden die hochgeladenen Videos einsortiert und geloggt wo jede Datei einsortiert wurde.</p> </div> + {% if sorterrorlog %} + <div class="table-responsive" style="max-height: 250px"> + <table class="table table-condensed"> + <tr> + <th></th> + <th>Zeit</th> + <th>Pfad</th> + <th>Course</th> + <th>Matches</th> + </tr> + {% for i in sorterrorlog %} + <tr class="danger"> + <td>{{ moderator_delete(['sorterrorlog',i.id,'deleted']) }}</td> + <td>{{i.when}}</td> + <td><a href="{{ config.VIDEOPREFIX }}/{{i.path}}">{{i.path}}</a></td> + <td><a href="{{url_for('course', id=i.course_id)}}">{{i.course_id}}</a></td> + <td> + {% for j in i.matches.split(',') %} + {% if not loop.first %},{% endif %} + <a href="{{url_for('course', id=i.course_id)}}#lecture-{{j}}">{{j}}</a> + {% endfor %} + </td> + </tr> + {% endfor %} + </table> + </div> + {% endif %} <div class="table-responsive"> <table class="table table-condensed"> <tr> @@ -21,9 +51,9 @@ {% for i in sortlog %} <tr> <td>{{i.when}}</td> - <td>{{i.path}}</td> - <td>{{i.course_id}}</td> - <td>{{i.lecture_id}}</td> + <td><a href="{{ config.VIDEOPREFIX }}/{{i.path}}">{{i.path}}</a></td> + <td><a href="{{url_for('course', id=i.course_id)}}">{{i.course_id}}</a></td> + <td><a href="{{url_for('course', id=i.course_id)}}#lecture-{{i.lecture_id}}">{{i.lecture_id}}</a></td> <td>{{i.id}}</td> </tr> {% endfor %}