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 disconnected Socket.IO sessions. It is used ' to signal associated background tasks to stop. ''' disconnected = [] @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. ''' socketio.start_background_task(background_task, current_app._get_current_object(), current_user.id, request.sid) @socketio.on('connect_admin') @login_required @admin_required def connect_admin(selected_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". ''' logger = logging.getLogger(__name__) logger.warning('Admin emitted "connect_admin".') logger.warning('Selected user id is: {}'.format(selected_user_id)) socketio.start_background_task(background_task_foreign, current_app._get_current_object(), selected_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). ''' disconnected.append(request.sid) def background_task(app, user_id, session_id): ''' ' 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' ''' with app.app_context(): user = db.session.query(User).filter_by(id=user_id).first() logging.getLogger(__name__) logging.warning('User object is: {}'.format(user)) ''' Get current values from the database. ''' corpora = user.corpora_as_dict() logging.warning('Corpora are: {}'.format(corpora)) jobs = user.jobs_as_dict() logging.warning('Jobs are: {}'.format(jobs)) ''' Send initial values. ''' socketio.emit('init-corpora', json.dumps(corpora), room=session_id) socketio.emit('init-jobs', json.dumps(jobs), room=session_id) ''' TODO: Implement maximum runtime for this loop. ''' while session_id not in disconnected: ''' 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-corpora', corpus_patch.to_string(), room=session_id) if jobs_patch: socketio.emit('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) disconnected.remove(session_id) def background_task_foreign(app, user_id, session_id): ''' ' 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' ''' with app.app_context(): user = db.session.query(User).filter_by(id=user_id).first() logging.getLogger(__name__) logging.warning('User object is: {}'.format(user)) ''' Get current values from the database. ''' corpora = user.corpora_as_dict() logging.warning('Corpora are: {}'.format(corpora)) jobs = user.jobs_as_dict() logging.warning('Jobs are: {}'.format(jobs)) ''' Send initial values. ''' socketio.emit('init-foreign-corpora', json.dumps(corpora), room=session_id) socketio.emit('init-foreign-jobs', json.dumps(jobs), room=session_id) ''' TODO: Implement maximum runtime for this loop. ''' while session_id not in disconnected: ''' 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', corpus_patch.to_string(), room=session_id) if jobs_patch: socketio.emit('update-foreign-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) disconnected.remove(session_id)