from app import db, mail, socketio from app.email import create_message from app.models import ( Corpus, CorpusFile, Job, JobInput, JobResult, JobStatus, JobStatusMailNotificationLevel ) from datetime import datetime ############################################################################### # 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') 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') def ressource_after_insert_handler(mapper, connection, ressource): value = ressource.to_dict(backrefs=False, relationships=False) for relationship in mapper.relationships: value[relationship.key] = {} 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') def ressource_after_update_handler(mapper, connection, ressource): jsonpatch = [] for attr in db.inspect(ressource).attrs: # Don't handle changes in relationship fields if attr.key in mapper.relationships: continue # Check if their are changes for the current field history = attr.load_history() if not history.has_changes(): continue if isinstance(history.added[0], datetime): # In order to be JSON serializable, DateTime attributes must be # converted to a string attr_name = attr.key value = history.added[0].isoformat() + 'Z' elif attr.key.endswith('_enum_value'): # Handling fake enum attributes attr_name = attr.key[:-11] value = getattr(ressource, attr_name).name else: attr_name = attr.key value = history.added[0] jsonpatch.append( { 'op': 'replace', 'path': f'{ressource.jsonpatch_path}/{attr_name}', 'value': value } ) # Job status update notification if it changed and wanted by the user if isinstance(ressource, Job) and attr_name == 'status': if ressource.user.setting_job_status_mail_notification_level == JobStatusMailNotificationLevel.NONE: # noqa pass elif ( ressource.user.setting_job_status_mail_notification_level == JobStatusMailNotificationLevel.END # noqa and ressource.status not in [JobStatus.COMPLETED, JobStatus.FAILED] # noqa ): 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)