diff --git a/app/__init__.py b/app/__init__.py index c08a4cf3a6cbcdfba3c8d223f14b2a58d5a227c0..aa40c14b5ef89925bc0f22a8db40d2aecbcfdae3 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -23,6 +23,8 @@ def create_app(config_name): mail.init_app(app) socketio.init_app(app, message_qeue='redis://') + from . import events + from .admin import admin as admin_blueprint app.register_blueprint(admin_blueprint, url_prefix='/admin') diff --git a/app/corpora/__init__.py b/app/corpora/__init__.py index 85739bc77ed41896b6f21b6032c3401fd1b30bc6..8323ec6a7415969e4ddcb67dfc2ab4f18c8031c0 100644 --- a/app/corpora/__init__.py +++ b/app/corpora/__init__.py @@ -2,5 +2,4 @@ from flask import Blueprint corpora = Blueprint('corpora', __name__) - -from . import views +from . import events, views diff --git a/app/corpora/events.py b/app/corpora/events.py new file mode 100644 index 0000000000000000000000000000000000000000..51b240ac7ce00b46abe1fa13a7f7ea79171ad472 --- /dev/null +++ b/app/corpora/events.py @@ -0,0 +1,34 @@ +from app import db, socketio +from app.events import connected_sessions +from app.models import Corpus +from flask import current_app, request +from flask_login import login_required +import logging + + +@socketio.on('init_corpus_analysis') +@login_required +def init_corpus_analysis(corpus_id): + ''' TODO: Check if current_user is allowed to subscribe to this ''' + socketio.start_background_task(observe_corpus_analysis_connection, + current_app._get_current_object(), + corpus_id, + request.sid) + + +@socketio.on('query_event') +def recv_query(message): + logger = logging.getLogger(__name__) + logger.warning(message) + + +def observe_corpus_analysis_connection(app, corpus_id, session_id): + logger = logging.getLogger(__name__) + with app.app_context(): + while session_id in connected_sessions: + logger.warning('Run container, run!') + socketio.sleep(3) + corpus = Corpus.query.filter_by(id=corpus_id).first() + corpus.status = 'stop analysis' + db.session.commit() + logger.warning('Stop container, stop!') diff --git a/app/main/events.py b/app/events.py similarity index 87% rename from app/main/events.py rename to app/events.py index c8ef6259f9aecdd768e500efe6ee83cc7e7f8b02..14ae3965de9de2721666aa30a5d75acdb5338c02 100644 --- a/app/main/events.py +++ b/app/events.py @@ -1,18 +1,18 @@ 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 +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. +' A list containing session ids of connected Socket.IO sessions. It is used to +' determine runtimes of associated background tasks. ''' -disconnected = [] +connected_sessions = [] @socketio.on('connect') @@ -24,12 +24,23 @@ def connect(): ' 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 removed from + ' connected sessions list (see above). + ''' + connected_sessions.remove(request.sid) + + @socketio.on('inspect_user') @login_required @admin_required @@ -48,22 +59,6 @@ def inspect_user(user_id): True) -@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) - - -@socketio.on('query_event') -def recv_query(message): - logger = logging.getLogger(__name__) - logger.warning(message) - - def background_task(app, user_id, session_id, foreign=False): ''' ' Sends initial corpus and job lists to the client. Afterwards it checks @@ -88,7 +83,7 @@ def background_task(app, user_id, session_id, foreign=False): json.dumps(jobs), room=session_id) ''' TODO: Implement maximum runtime for this loop. ''' - while session_id not in disconnected: + while session_id in connected_sessions: ''' Get current values from the database ''' new_corpora = user.corpora_as_dict() new_jobs = user.jobs_as_dict() @@ -108,4 +103,3 @@ def background_task(app, user_id, session_id, foreign=False): corpora = new_corpora jobs = new_jobs socketio.sleep(3) - disconnected.remove(session_id) diff --git a/app/main/__init__.py b/app/main/__init__.py index 356af527a684ffa71b5866f139da0bdd76137072..1baeacf6e50e47f921c7182602266cf1701113e1 100644 --- a/app/main/__init__.py +++ b/app/main/__init__.py @@ -3,7 +3,7 @@ from flask import Blueprint main = Blueprint('main', __name__) -from . import errors, events, views +from . import errors, views from ..models import Permission diff --git a/app/models.py b/app/models.py index 07a1f9e6a7d264b0a1fb0f4957a619c5c5dd57e6..c7d5563a1105cd518369b2c89a0b75feb86b1918 100644 --- a/app/models.py +++ b/app/models.py @@ -7,7 +7,6 @@ from . import db from . import login_manager import os import shutil -import logging import xml.etree.ElementTree as ET @@ -226,13 +225,10 @@ class User(UserMixin, db.Model): Delete user from database. Also delete all associated jobs and corpora files. """ - logger = logging.getLogger(__name__) delete_path = os.path.join('/mnt/opaque/', str(self.id)) - logger.warning('Delete path for user is: {}'.format(delete_path)) while os.path.exists(delete_path): try: shutil.rmtree(delete_path, ignore_errors=True) - logger.warning('Path does still exist.') except OSError: pass db.session.delete(self) @@ -383,14 +379,11 @@ class Job(db.Model): for good. See: https://docs.docker.com/engine/swarm/swarm-tutorial/delete-service/ """ - logger = logging.getLogger(__name__) delete_path = os.path.join('/mnt/opaque/', str(self.user_id), 'jobs', str(self.id)) - logger.warning('Delete path is: {}'.format(delete_path)) while os.path.exists(delete_path): try: shutil.rmtree(delete_path, ignore_errors=True) - logger.warning('Path does still exist.') except OSError: pass db.session.delete(self) @@ -412,15 +405,12 @@ class CorpusFile(db.Model): corpus_id = db.Column(db.Integer, db.ForeignKey('corpora.id')) def delete(self): - logger = logging.getLogger(__name__) - logger.warning('Called CorpusFile.delete') path = os.path.join(current_app.config['OPAQUE_STORAGE_DIRECTORY'], self.dir, self.filename) try: os.remove(path) except: - logger.warning('[ERROR] CorpusFile.delete') return self.corpus.status = 'unprepared' db.session.delete(self) @@ -474,24 +464,15 @@ class Corpus(db.Model): 'user_id': self.user_id} def delete(self): - logger = logging.getLogger(__name__) - logger.warning('Called Corpus.delete') for corpus_file in self.files: corpus_file.delete() - logger.warning('bis hierhin und nicht weiter') - logger.warning('base_dir: {}'.format(current_app.config['OPAQUE_STORAGE_DIRECTORY'])) - logger.warning('user_id: {}'.format(self.user_id)) - logger.warning('id: {}'.format(self.id)) path = os.path.join(current_app.config['OPAQUE_STORAGE_DIRECTORY'], str(self.user_id), 'corpora', str(self.id)) - logger.warning(path) try: - logger.warning('Try to remove {}'.format(path)) shutil.rmtree(path) except: - logger.warning('[ERROR] Corpus.delete') return db.session.delete(self) db.session.commit() diff --git a/app/templates/corpora/corpus_analysis.html.j2 b/app/templates/corpora/corpus_analysis.html.j2 index bd2eb46eeeea565b597c78de19b7df64790ba510..39599272d16f5655252e14e5ab65afcd1b5d7675 100644 --- a/app/templates/corpora/corpus_analysis.html.j2 +++ b/app/templates/corpora/corpus_analysis.html.j2 @@ -1,6 +1,11 @@ {% extends "full_width.html.j2" %} {% block page_content %} +<script> +socket.emit('init_corpus_analysis', {{ corpus_id }}); +</script> + + <div class="col s12 m3 l3 sticky"> <a class="waves-effect waves-light btn" href="{{ url_for('corpora.corpus', corpus_id=corpus_id) }}"><i class="material-icons left">arrow_back</i>Back to corpus overview</a>