from flask import current_app, request from flask_login import current_user, login_required from .decorators import admin_required from . import db, socketio from .models import User import json import jsonpatch import logging ''' ' A list containing session ids of connected Socket.IO sessions. It is used to ' determine runtimes of associated background tasks. ''' connected_sessions = [] @socketio.on('connect') @login_required def connect(): ''' ' The Socket.IO module creates a session id (sid) on each request. The ' initiating client is automatically placed in a room with that sid, which ' will be used for further information exchange generated by a background ' task associated with the sid. ''' connected_sessions.append(request.sid) socketio.start_background_task(background_task, current_app._get_current_object(), current_user.id, request.sid) @socketio.on('disconnect') @login_required def disconnect(): ''' ' On disconnect the session id (sid) of the connection gets placed in the ' disconnected list (see above). ''' connected_sessions.remove(request.sid) @socketio.on('inspect_user') @login_required @admin_required def inspect_user(user_id): ''' ' The Socket.IO module creates a session id (sid) on each request. The ' initiating admin is automatically placed in a room with that sid, which ' will be used for further information exchange generated by a background ' task associated with the sid. Admin will be placed in that room on emiting ' "conncect_admin". ''' socketio.start_background_task(background_task, current_app._get_current_object(), user_id, request.sid, True) def background_task(app, user_id, session_id, foreign=False): ''' ' Sends initial corpus and job lists to the client. Afterwards it checks ' every 3 seconds if changes to the initial values appeared. If changes are ' detected, a RFC 6902 compliant JSON patch gets send. ' ' NOTE: The initial values are send as a init-* events. ' The JSON patches are send as update-* events. ' ' > where '*' is either 'corpora' or 'jobs' ''' logger = logging.getLogger(__name__) with app.app_context(): user = db.session.query(User).get_or_404(user_id) ''' Get current values from the database. ''' corpora = user.corpora_as_dict() jobs = user.jobs_as_dict() ''' Send initial values. ''' socketio.emit('init-foreign-corpora' if foreign else 'init-corpora', json.dumps(corpora), room=session_id) socketio.emit('init-foreign-jobs' if foreign else 'init-jobs', json.dumps(jobs), room=session_id) ''' TODO: Implement maximum runtime for this loop. ''' while session_id in connected_sessions: logger.warning('Running...') ''' Get current values from the database ''' new_corpora = user.corpora_as_dict() new_jobs = user.jobs_as_dict() ''' Compute JSON patches. ''' corpus_patch = jsonpatch.JsonPatch.from_diff(corpora, new_corpora) jobs_patch = jsonpatch.JsonPatch.from_diff(jobs, new_jobs) ''' In case there are patches, send them to the user. ''' if corpus_patch: socketio.emit('update-foreign-corpora' if foreign else 'update-corpora', corpus_patch.to_string(), room=session_id) if jobs_patch: socketio.emit('update-foreign-jobs' if foreign else 'update-jobs', jobs_patch.to_string(), room=session_id) ''' Set new values as references for the next iteration. ''' corpora = new_corpora jobs = new_jobs socketio.sleep(3) logger.warning('Stoping!')