from datetime import datetime from flask import current_app from .. import db, mail, socketio from ..email import create_message from ..models import Corpus, CorpusFile, Job, JobInput, JobResult, QueryResult ############################################################################### # SQLAlchemy event handlers # ############################################################################### @db.event.listens_for(Corpus, 'after_delete') @db.event.listens_for(CorpusFile, 'after_delete') @db.event.listens_for(Job, 'after_delete') @db.event.listens_for(JobInput, 'after_delete') @db.event.listens_for(JobResult, 'after_delete') @db.event.listens_for(QueryResult, 'after_delete') def ressource_after_delete(mapper, connection, ressource): jsonpatch = [{'op': 'remove', 'path': ressource.jsonpatch_path}] room = f'users.{ressource.user_hashid}' socketio.emit('users.patch', jsonpatch, room=room) @db.event.listens_for(Corpus, 'after_insert') @db.event.listens_for(CorpusFile, 'after_insert') @db.event.listens_for(Job, 'after_insert') @db.event.listens_for(JobInput, 'after_insert') @db.event.listens_for(JobResult, 'after_insert') @db.event.listens_for(QueryResult, 'after_insert') def ressource_after_insert_handler(mapper, connection, ressource): value = ressource.to_dict(backrefs=False, relationships=False) jsonpatch = [ {'op': 'add', 'path': ressource.jsonpatch_path, 'value': value} ] room = f'users.{ressource.user_hashid}' socketio.emit('users.patch', jsonpatch, room=room) @db.event.listens_for(Corpus, 'after_update') @db.event.listens_for(CorpusFile, 'after_update') @db.event.listens_for(Job, 'after_update') @db.event.listens_for(JobInput, 'after_update') @db.event.listens_for(JobResult, 'after_update') @db.event.listens_for(QueryResult, 'after_update') def ressource_after_update_handler(mapper, connection, ressource): jsonpatch = [] for attr in db.inspect(ressource).attrs: # We don't want to handle changes in relationship fields # TODO: Find a way to handle this without a hardcoded list if attr.key in ['files', 'inputs', 'results']: continue history = attr.load_history() if not history.has_changes(): continue new_value = history.added[0] # In order to be JSON serializable, DateTime attributes must be # converted to a string if isinstance(new_value, datetime): new_value = new_value.isoformat() + 'Z' jsonpatch.append( { 'op': 'replace', 'path': f'{ressource.jsonpatch_path}/{attr.key}', 'value': new_value } ) # Job status update notification if it changed and wanted by the user if isinstance(ressource, Job) and attr.key == 'status': if ressource.user.setting_job_status_mail_notifications == 'none': # noqa pass elif (ressource.user.setting_job_status_mail_notifications == 'end' # noqa and ressource.status not in ['complete', 'failed']): pass else: msg = create_message( ressource.user.email, f'Status update for your Job "{ressource.title}"', 'tasks/email/notification', job=ressource ) mail.send(msg) if jsonpatch: room = f'users.{ressource.user_hashid}' socketio.emit('users.patch', jsonpatch, room=room)