make is_administrator a property, add back db events

This commit is contained in:
Patrick Jentsch 2024-04-11 14:33:47 +02:00
parent d0d2a8abd6
commit ccf484c9bc
24 changed files with 205 additions and 74 deletions

View File

@ -77,7 +77,7 @@ def delete_job(job_id):
job = Job.query.get(job_id)
if job is None:
abort(404)
if not (job.user == current_user or current_user.is_administrator()):
if not (job.user == current_user or current_user.is_administrator):
abort(403)
try:
job.delete()
@ -97,6 +97,6 @@ def get_job(job_id):
job = Job.query.get(job_id)
if job is None:
abort(404)
if not (job.user == current_user or current_user.is_administrator()):
if not (job.user == current_user or current_user.is_administrator):
abort(403)
return job

View File

@ -60,7 +60,7 @@ def delete_user(user_id):
user = User.query.get(user_id)
if user is None:
abort(404)
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
abort(403)
user.delete()
db.session.commit()
@ -78,7 +78,7 @@ def get_user(user_id):
user = User.query.get(user_id)
if user is None:
abort(404)
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
abort(403)
return user
@ -94,6 +94,6 @@ def get_user_by_username(username):
user = User.query.filter(User.username == username).first()
if user is None:
abort(404)
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
abort(403)
return user

View File

@ -17,7 +17,7 @@ def delete_spacy_model(spacy_nlp_pipeline_model_id):
db.session.commit()
snpm = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id)
if not (snpm.user == current_user or current_user.is_administrator()):
if not (snpm.user == current_user or current_user.is_administrator):
abort(403)
thread = Thread(
target=_delete_spacy_model,
@ -39,7 +39,7 @@ def update_spacy_nlp_pipeline_model_is_public(spacy_nlp_pipeline_model_id):
if not isinstance(is_public, bool):
abort(400)
snpm = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id)
if not (snpm.user == current_user or current_user.is_administrator()):
if not (snpm.user == current_user or current_user.is_administrator):
abort(403)
snpm.is_public = is_public
db.session.commit()

View File

@ -58,7 +58,7 @@ def create_spacy_nlp_pipeline_model():
@register_breadcrumb(bp, '.spacy_nlp_pipeline_models.entity', '', dynamic_list_constructor=spacy_nlp_pipeline_model_dlc)
def spacy_nlp_pipeline_model(spacy_nlp_pipeline_model_id):
snpm = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id)
if not (snpm.user == current_user or current_user.is_administrator()):
if not (snpm.user == current_user or current_user.is_administrator):
abort(403)
form = UpdateSpaCyNLPPipelineModelForm(data=snpm.to_json_serializeable())
if form.validate_on_submit():

View File

@ -17,7 +17,7 @@ def delete_tesseract_model(tesseract_ocr_pipeline_model_id):
db.session.commit()
topm = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id)
if not (topm.user == current_user or current_user.is_administrator()):
if not (topm.user == current_user or current_user.is_administrator):
abort(403)
thread = Thread(
target=_delete_tesseract_ocr_pipeline_model,
@ -39,7 +39,7 @@ def update_tesseract_ocr_pipeline_model_is_public(tesseract_ocr_pipeline_model_i
if not isinstance(is_public, bool):
abort(400)
topm = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id)
if not (topm.user == current_user or current_user.is_administrator()):
if not (topm.user == current_user or current_user.is_administrator):
abort(403)
topm.is_public = is_public
db.session.commit()

View File

@ -57,7 +57,7 @@ def create_tesseract_ocr_pipeline_model():
@register_breadcrumb(bp, '.tesseract_ocr_pipeline_models.entity', '', dynamic_list_constructor=tesseract_ocr_pipeline_model_dlc)
def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id):
topm = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id)
if not (topm.user == current_user or current_user.is_administrator()):
if not (topm.user == current_user or current_user.is_administrator):
abort(403)
form = UpdateTesseractOCRPipelineModelForm(data=topm.to_json_serializeable())
if form.validate_on_submit():

View File

@ -99,7 +99,7 @@ class CQiNamespace(Namespace):
return {'code': 404, 'msg': 'Not Found'}
if not (db_corpus.user == current_user
or current_user.is_following_corpus(db_corpus)
or current_user.is_administrator()):
or current_user.is_administrator):
return {'code': 403, 'msg': 'Forbidden'}
if db_corpus.status not in [
CorpusStatus.BUILT,

View File

@ -10,7 +10,7 @@ def corpus_follower_permission_required(*permissions):
def decorated_function(*args, **kwargs):
corpus_id = kwargs.get('corpus_id')
corpus = Corpus.query.get_or_404(corpus_id)
if not (corpus.user == current_user or current_user.is_administrator()):
if not (corpus.user == current_user or current_user.is_administrator):
cfa = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=current_user.id).first()
if cfa is None:
abort(403)
@ -26,7 +26,7 @@ def corpus_owner_or_admin_required(f):
def decorated_function(*args, **kwargs):
corpus_id = kwargs.get('corpus_id')
corpus = Corpus.query.get_or_404(corpus_id)
if not (corpus.user == current_user or current_user.is_administrator()):
if not (corpus.user == current_user or current_user.is_administrator):
abort(403)
return f(*args, **kwargs)
return decorated_function

View File

@ -15,7 +15,7 @@ def get_corpus(corpus_hashid):
if not (
corpus.is_public
or corpus.user == current_user
or current_user.is_administrator()
or current_user.is_administrator
):
return {'options': {'status': 403, 'statusText': 'Forbidden'}}
return {
@ -38,7 +38,7 @@ def subscribe_corpus(corpus_hashid):
if not (
corpus.is_public
or corpus.user == current_user
or current_user.is_administrator()
or current_user.is_administrator
):
return {'options': {'status': 403, 'statusText': 'Forbidden'}}
join_room(f'/corpora/{corpus.hashid}')

View File

@ -58,7 +58,7 @@ def delete_corpus_follower(corpus_id, follower_id):
current_user.id == follower_id
or current_user == cfa.corpus.user
or CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=current_user.id).first().role.has_permission('MANAGE_FOLLOWERS')
or current_user.is_administrator()):
or current_user.is_administrator):
abort(403)
if current_user.id == follower_id:
flash(f'You are no longer following "{cfa.corpus.title}"', 'corpus')

View File

@ -55,13 +55,13 @@ def corpus(corpus_id):
users = User.query.filter(User.is_public == True, User.id != current_user.id, User.id != corpus.user.id, User.role_id < 4).all()
cfa = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=current_user.id).first()
if cfa is None:
if corpus.user == current_user or current_user.is_administrator():
if corpus.user == current_user or current_user.is_administrator:
cfr = CorpusFollowerRole.query.filter_by(name='Administrator').first()
else:
cfr = CorpusFollowerRole.query.filter_by(name='Anonymous').first()
else:
cfr = cfa.role
if corpus.user == current_user or current_user.is_administrator():
if corpus.user == current_user or current_user.is_administrator:
return render_template(
'corpora/corpus.html.j2',
title=corpus.title,

View File

@ -17,7 +17,7 @@ def delete_job(job_id):
db.session.commit()
job = Job.query.get_or_404(job_id)
if not (job.user == current_user or current_user.is_administrator()):
if not (job.user == current_user or current_user.is_administrator):
abort(403)
thread = Thread(
target=_delete_job,
@ -56,7 +56,7 @@ def restart_job(job_id):
db.session.commit()
job = Job.query.get_or_404(job_id)
if not (job.user == current_user or current_user.is_administrator()):
if not (job.user == current_user or current_user.is_administrator):
abort(403)
if job.status == JobStatus.FAILED:
response = {'errors': {'message': 'Job status is not "failed"'}}

View File

@ -22,7 +22,7 @@ def corpora():
@register_breadcrumb(bp, '.entity', '', dynamic_list_constructor=job_dlc)
def job(job_id):
job = Job.query.get_or_404(job_id)
if not (job.user == current_user or current_user.is_administrator()):
if not (job.user == current_user or current_user.is_administrator):
abort(403)
return render_template(
'jobs/job.html.j2',
@ -34,7 +34,7 @@ def job(job_id):
@bp.route('/<hashid:job_id>/inputs/<hashid:job_input_id>/download')
def download_job_input(job_id, job_input_id):
job_input = JobInput.query.filter_by(job_id=job_id, id=job_input_id).first_or_404()
if not (job_input.job.user == current_user or current_user.is_administrator()):
if not (job_input.job.user == current_user or current_user.is_administrator):
abort(403)
return send_from_directory(
job_input.path.parent,
@ -48,7 +48,7 @@ def download_job_input(job_id, job_input_id):
@bp.route('/<hashid:job_id>/results/<hashid:job_result_id>/download')
def download_job_result(job_id, job_result_id):
job_result = JobResult.query.filter_by(job_id=job_id, id=job_result_id).first_or_404()
if not (job_result.job.user == current_user or current_user.is_administrator()):
if not (job_result.job.user == current_user or current_user.is_administrator):
abort(403)
return send_from_directory(
job_result.path.parent,

View File

@ -1,3 +1,6 @@
from enum import Enum
from app import db, login, mail, socketio
from app.email import create_message
from .avatar import *
from .corpus_file import *
from .corpus_follower_association import *
@ -11,7 +14,132 @@ from .spacy_nlp_pipeline_model import *
from .tesseract_ocr_pipeline_model import *
from .token import *
from .user import *
from app import login
@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(SpaCyNLPPipelineModel, 'after_delete')
@db.event.listens_for(TesseractOCRPipelineModel, 'after_delete')
def resource_after_delete(mapper, connection, resource):
print('[START] resource_after_delete')
jsonpatch = [
{
'op': 'remove',
'path': resource.jsonpatch_path
}
]
room = f'/users/{resource.user_hashid}'
print('[EMIT] PATCH', jsonpatch)
socketio.emit('PATCH', jsonpatch, room=room)
print('[END] resource_after_delete')
@db.event.listens_for(CorpusFollowerAssociation, 'after_delete')
def cfa_after_delete_handler(mapper, connection, cfa):
jsonpatch_path = f'/users/{cfa.corpus.user.hashid}/corpora/{cfa.corpus.hashid}/corpus_follower_associations/{cfa.hashid}'
jsonpatch = [
{
'op': 'remove',
'path': jsonpatch_path
}
]
room = f'/users/{cfa.corpus.user.hashid}'
socketio.emit('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(SpaCyNLPPipelineModel, 'after_insert')
@db.event.listens_for(TesseractOCRPipelineModel, 'after_insert')
def resource_after_insert_handler(mapper, connection, resource):
jsonpatch_value = resource.to_json_serializeable()
for attr in mapper.relationships:
jsonpatch_value[attr.key] = {}
jsonpatch = [
{
'op': 'add',
'path': resource.jsonpatch_path,
'value': jsonpatch_value
}
]
room = f'/users/{resource.user_hashid}'
socketio.emit('PATCH', jsonpatch, room=room)
@db.event.listens_for(CorpusFollowerAssociation, 'after_insert')
def cfa_after_insert_handler(mapper, connection, cfa):
jsonpatch_value = cfa.to_json_serializeable()
jsonpatch_path = f'/users/{cfa.corpus.user.hashid}/corpora/{cfa.corpus.hashid}/corpus_follower_associations/{cfa.hashid}'
jsonpatch = [
{
'op': 'add',
'path': jsonpatch_path,
'value': jsonpatch_value
}
]
room = f'/users/{cfa.corpus.user.hashid}'
socketio.emit('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(SpaCyNLPPipelineModel, 'after_update')
@db.event.listens_for(TesseractOCRPipelineModel, 'after_update')
def resource_after_update_handler(mapper, connection, resource):
jsonpatch = []
for attr in db.inspect(resource).attrs:
if attr.key in mapper.relationships:
continue
if not attr.load_history().has_changes():
continue
jsonpatch_path = f'{resource.jsonpatch_path}/{attr.key}'
if isinstance(attr.value, datetime):
jsonpatch_value = f'{attr.value.isoformat()}Z'
elif isinstance(attr.value, Enum):
jsonpatch_value = attr.value.name
else:
jsonpatch_value = attr.value
jsonpatch.append(
{
'op': 'replace',
'path': jsonpatch_path,
'value': jsonpatch_value
}
)
if jsonpatch:
room = f'/users/{resource.user_hashid}'
socketio.emit('PATCH', jsonpatch, room=room)
@db.event.listens_for(Job, 'after_update')
def job_after_update_handler(mapper, connection, job):
for attr in db.inspect(job).attrs:
if attr.key != 'status':
continue
if not attr.load_history().has_changes():
return
if job.user.setting_job_status_mail_notification_level == UserSettingJobStatusMailNotificationLevel.NONE:
return
if job.user.setting_job_status_mail_notification_level == UserSettingJobStatusMailNotificationLevel.END:
if job.status not in [JobStatus.COMPLETED, JobStatus.FAILED]:
return
msg = create_message(
job.user.email,
f'Status update for your Job "{job.title}"',
'tasks/email/notification',
job=job
)
mail.send(msg)
@login.user_loader

View File

@ -132,6 +132,10 @@ class User(HashidMixin, UserMixin, db.Model):
def __repr__(self):
return f'<User {self.username}>'
@property
def is_administrator(self):
return self.can(Permission.ADMINISTRATE)
@property
def jsonpatch_path(self):
return f'/users/{self.hashid}'
@ -294,9 +298,6 @@ class User(HashidMixin, UserMixin, db.Model):
algorithm='HS256'
)
def is_administrator(self):
return self.can(Permission.ADMINISTRATE)
def ping(self):
self.last_seen = datetime.utcnow()

View File

@ -120,7 +120,7 @@
{% block modals %}
{{ super() }}
{% if current_user == corpus.user or current_user.is_administrator() %}
{% if current_user == corpus.user or current_user.is_administrator %}
<div class="modal" id="publishing-modal">
<div class="modal-content">
<h4>Change your Corpus publishing status</h4>
@ -247,7 +247,7 @@
});
{% endif %}
{% if current_user == corpus.user or current_user.is_administrator() %}
{% if current_user == corpus.user or current_user.is_administrator %}
// #region Publishing
let publishingModalIsPublicSwitchElement = document.querySelector('#publishing-modal-is-public-switch');
publishingModalIsPublicSwitchElement.addEventListener('change', (event) => {

View File

@ -77,7 +77,7 @@
</div>
</div>
<div class="card-action right-align">
{% if current_user.is_administrator() %}
{% if current_user.is_administrator %}
<a class="action-button btn disabled waves-effect waves-light modal-trigger" data-action="get-log-request" id="job-log-button" href="#job-log-modal"><i class="material-icons left">text_snippet</i>Log</a>
{% endif %}
<a class="btn disabled waves-effect waves-light modal-trigger" href="#restart-job-modal"><i class="material-icons left">repeat</i>Restart</a>
@ -159,7 +159,7 @@
nopaque.requests.jobs.entity.restart({{ job.hashid|tojson }});
});
if ({{ current_user.is_administrator()|tojson }}) {
if ({{ current_user.is_administrator|tojson }}) {
let jobLogButtonElement = document.querySelector('#job-log-button');
jobLogButtonElement.addEventListener('click', (event) => {
nopaque.requests.jobs.entity.log({{ job.hashid|tojson }})

View File

@ -12,7 +12,7 @@ def get_user(user_hashid):
user = User.query.get(user_id)
if user is None:
return {'status': 404, 'statusText': 'Not found'}
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
return {'status': 403, 'statusText': 'Forbidden'}
return {
'body': user.to_json_serializeable(backrefs=True, relationships=True),
@ -28,7 +28,7 @@ def subscribe_user(user_hashid):
user = User.query.get(user_id)
if user is None:
return {'status': 404, 'statusText': 'Not found'}
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
return {'status': 403, 'statusText': 'Forbidden'}
join_room(f'/users/{user.hashid}')
return {'status': 200, 'statusText': 'OK'}
@ -41,7 +41,7 @@ def unsubscribe_user(user_hashid):
user = User.query.get(user_id)
if user is None:
return {'status': 404, 'statusText': 'Not found'}
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
return {'status': 403, 'statusText': 'Forbidden'}
leave_room(f'/users/{user.hashid}')
return {'status': 200, 'statusText': 'OK'}

View File

@ -17,7 +17,7 @@ def delete_user(user_id):
db.session.commit()
user = User.query.get_or_404(user_id)
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
abort(403)
thread = Thread(
target=_delete_user,
@ -44,7 +44,7 @@ def delete_user_avatar(user_id):
user = User.query.get_or_404(user_id)
if user.avatar is None:
abort(404)
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
abort(403)
thread = Thread(
target=_delete_avatar,

View File

@ -33,7 +33,7 @@ def get_user(user_hashid):
user = User.query.get(user_id)
if user is None:
return {'options': {'status': 404, 'statusText': 'Not found'}}
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
return {'options': {'status': 403, 'statusText': 'Forbidden'}}
return {
'body': user.to_json_serializable(),
@ -52,7 +52,7 @@ def subscribe_user(user_hashid):
user = User.query.get(user_id)
if user is None:
return {'options': {'status': 404, 'statusText': 'Not found'}}
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
return {'options': {'status': 403, 'statusText': 'Forbidden'}}
join_room(f'/users/{user.hashid}')
return {'options': {'status': 200, 'statusText': 'OK'}}
@ -89,7 +89,7 @@ def get_user(user_hashid):
user = User.query.filter_by(id=user_id, is_public=True).first()
if user is None:
return {'options': {'status': 404, 'statusText': 'Not found'}}
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
return {'options': {'status': 403, 'statusText': 'Forbidden'}}
return {
'body': user.to_json_serializable(filter_by_privacy_settings=True),
@ -108,7 +108,7 @@ def subscribe_user(user_hashid):
user = User.query.filter_by(id=user_id, is_public=True).first()
if user is None:
return {'options': {'status': 404, 'statusText': 'Not found'}}
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
return {'options': {'status': 403, 'statusText': 'Forbidden'}}
join_room(f'/public_users/{user.hashid}')
return {'options': {'status': 200, 'statusText': 'OK'}}

View File

@ -22,7 +22,7 @@ def users():
@register_breadcrumb(bp, '.entity', '', dynamic_list_constructor=user_dlc)
def user(user_id):
user = User.query.get_or_404(user_id)
if not (user.is_public or user == current_user or current_user.is_administrator()):
if not (user.is_public or user == current_user or current_user.is_administrator):
abort(403)
return render_template(
'users/user.html.j2',
@ -34,7 +34,7 @@ def user(user_id):
@bp.route('/<hashid:user_id>/avatar')
def user_avatar(user_id):
user = User.query.get_or_404(user_id)
if not (user.is_public or user == current_user or current_user.is_administrator()):
if not (user.is_public or user == current_user or current_user.is_administrator):
abort(403)
if user.avatar is None:
return redirect(url_for('static', filename='images/user_avatar.png'))

View File

@ -10,7 +10,7 @@ from . import bp
@content_negotiation(consumes='application/json', produces='application/json')
def update_user_profile_privacy_setting_is_public(user_id):
user = User.query.get_or_404(user_id)
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
abort(403)
enabled = request.json
if not isinstance(enabled, bool):
@ -32,7 +32,7 @@ def update_user_profile_privacy_settings(user_id, profile_privacy_setting_name):
profile_privacy_setting = ProfilePrivacySettings[profile_privacy_setting_name]
except KeyError:
abort(404)
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
abort(403)
enabled = request.json
if not isinstance(enabled, bool):

View File

@ -18,7 +18,7 @@ from .forms import (
@register_breadcrumb(bp, '.entity.settings', '<i class="material-icons left">settings</i>Settings', endpoint_arguments_constructor=user_eac)
def settings(user_id):
user = User.query.get_or_404(user_id)
if not (user == current_user or current_user.is_administrator()):
if not (user == current_user or current_user.is_administrator):
abort(403)
redirect_location_on_post = g.pop(

58
wsgi.py
View File

@ -3,23 +3,25 @@
import eventlet
eventlet.monkey_patch()
from app import create_app, db, scheduler, socketio # noqa
from app.models import (
Avatar,
Corpus,
CorpusFile,
CorpusFollowerAssociation,
CorpusFollowerRole,
Job,
JobInput,
JobResult,
Role,
TesseractOCRPipelineModel,
SpaCyNLPPipelineModel,
User
) # noqa
from flask import Flask # noqa
from typing import Any, Dict # noqa
from app import create_app, db, scheduler, socketio # noqa
from app import models # noqa
# from app.models import (
# Avatar,
# Corpus,
# CorpusFile,
# CorpusFollowerAssociation,
# CorpusFollowerRole,
# Job,
# JobInput,
# JobResult,
# Role,
# TesseractOCRPipelineModel,
# SpaCyNLPPipelineModel,
# User
# ) # noqa
app: Flask = create_app()
@ -30,25 +32,25 @@ def make_shell_context() -> Dict[str, Any]:
''' Adds variables to the shell context. '''
return {
'db': db,
'Avatar': Avatar,
'Corpus': Corpus,
'CorpusFile': CorpusFile,
'CorpusFollowerAssociation': CorpusFollowerAssociation,
'CorpusFollowerRole': CorpusFollowerRole,
'Job': Job,
'JobInput': JobInput,
'JobResult': JobResult,
'Role': Role,
'TesseractOCRPipelineModel': TesseractOCRPipelineModel,
'SpaCyNLPPipelineModel': SpaCyNLPPipelineModel,
'User': User
'Avatar': models.Avatar,
'Corpus': models.Corpus,
'CorpusFile': models.CorpusFile,
'CorpusFollowerAssociation': models.CorpusFollowerAssociation,
'CorpusFollowerRole': models.CorpusFollowerRole,
'Job': models.Job,
'JobInput': models.JobInput,
'JobResult': models.JobResult,
'Role': models.Role,
'TesseractOCRPipelineModel': models.TesseractOCRPipelineModel,
'SpaCyNLPPipelineModel': models.SpaCyNLPPipelineModel,
'User': models.User
}
def main():
with app.app_context():
if app.config['NOPAQUE_IS_PRIMARY_INSTANCE']:
for corpus in Corpus.query.filter(Corpus.num_analysis_sessions > 0).all():
for corpus in models.Corpus.query.filter(models.Corpus.num_analysis_sessions > 0).all():
corpus.num_analysis_sessions = 0
db.session.commit()
scheduler.start()