mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2025-06-15 18:40:40 +00:00
move blueprints in dedicated folder
This commit is contained in:
18
app/blueprints/jobs/__init__.py
Normal file
18
app/blueprints/jobs/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
from flask import Blueprint
|
||||
from flask_login import login_required
|
||||
|
||||
|
||||
bp = Blueprint('jobs', __name__)
|
||||
|
||||
|
||||
@bp.before_request
|
||||
@login_required
|
||||
def before_request():
|
||||
'''
|
||||
Ensures that the routes in this package can only be visited by users that
|
||||
are logged in.
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
from . import routes, json_routes
|
138
app/blueprints/jobs/events.py
Normal file
138
app/blueprints/jobs/events.py
Normal file
@ -0,0 +1,138 @@
|
||||
from flask import current_app
|
||||
from flask_login import current_user
|
||||
from flask_socketio import Namespace
|
||||
from app import db, hashids, socketio
|
||||
from app.extensions.flask_socketio import admin_required, login_required
|
||||
from app.models import Job, JobStatus
|
||||
|
||||
|
||||
class JobsNamespace(Namespace):
|
||||
@login_required
|
||||
def on_delete(self, job_hashid: str):
|
||||
# Decode the job hashid
|
||||
job_id = hashids.decode(job_hashid)
|
||||
|
||||
# Validate job_id
|
||||
if not isinstance(job_id, int):
|
||||
return {
|
||||
'code': 400,
|
||||
'body': 'job_id is invalid'
|
||||
}
|
||||
|
||||
# Load job from database
|
||||
job = Job.query.get(job_id)
|
||||
if job is None:
|
||||
return {
|
||||
'code': 404,
|
||||
'body': 'Job not found'
|
||||
}
|
||||
|
||||
# Check if the current user is allowed to delete the job
|
||||
if not (job.user == current_user or current_user.is_administrator):
|
||||
return {
|
||||
'code': 403,
|
||||
'body': 'Forbidden'
|
||||
}
|
||||
|
||||
# TODO: This should be a method in the Job model
|
||||
def _delete_job(app, job_id):
|
||||
with app.app_context():
|
||||
job = Job.query.get(job_id)
|
||||
job.delete()
|
||||
db.session.commit()
|
||||
|
||||
# Delete the job in a background task
|
||||
socketio.start_background_task(
|
||||
target=_delete_job,
|
||||
app=current_app._get_current_object(),
|
||||
job_id=job_id
|
||||
)
|
||||
|
||||
return {
|
||||
'code': 202,
|
||||
'body': f'Job "{job.title}" marked for deletion'
|
||||
}
|
||||
|
||||
|
||||
@admin_required
|
||||
def on_get_log(self, job_hashid: str):
|
||||
# Decode the job hashid
|
||||
job_id = hashids.decode(job_hashid)
|
||||
|
||||
# Validate job_id
|
||||
if not isinstance(job_id, int):
|
||||
return {
|
||||
'code': 400,
|
||||
'body': 'job_id is invalid'
|
||||
}
|
||||
|
||||
# Load job from database
|
||||
job = Job.query.get(job_id)
|
||||
if job is None:
|
||||
return {
|
||||
'code': 404,
|
||||
'body': 'Job not found'
|
||||
}
|
||||
|
||||
# Check if the job is already processed
|
||||
if job.status not in [JobStatus.COMPLETED, JobStatus.FAILED]:
|
||||
return {
|
||||
'code': 409,
|
||||
'body': 'Job is not done processing'
|
||||
}
|
||||
|
||||
# Read the log file
|
||||
with open(job.path / 'pipeline_data' / 'logs' / 'pyflow_log.txt') as log_file:
|
||||
job_log = log_file.read()
|
||||
|
||||
return {
|
||||
'code': 200,
|
||||
'body': job_log
|
||||
}
|
||||
|
||||
|
||||
@login_required
|
||||
def on_restart(self, job_hashid: str):
|
||||
# Decode the job hashid
|
||||
job_id = hashids.decode(job_hashid)
|
||||
|
||||
# Validate job_id
|
||||
if not isinstance(job_id, int):
|
||||
return {
|
||||
'code': 400,
|
||||
'body': 'job_id is invalid'
|
||||
}
|
||||
|
||||
# Load job from database
|
||||
job = Job.query.get(job_id)
|
||||
if job is None:
|
||||
return {
|
||||
'code': 404,
|
||||
'body': 'Job not found'
|
||||
}
|
||||
|
||||
# Check if the current user is allowed to restart the job
|
||||
if not (job.user == current_user or current_user.is_administrator):
|
||||
return {
|
||||
'code': 403,
|
||||
'body': 'Forbidden'
|
||||
}
|
||||
|
||||
# TODO: This should be a method in the Job model
|
||||
def _restart_job(app, job_id):
|
||||
with app.app_context():
|
||||
job = Job.query.get(job_id)
|
||||
job.restart()
|
||||
db.session.commit()
|
||||
|
||||
# Restart the job in a background task
|
||||
socketio.start_background_task(
|
||||
target=_restart_job,
|
||||
app=current_app._get_current_object(),
|
||||
job_id=job_id
|
||||
)
|
||||
|
||||
return {
|
||||
'code': 202,
|
||||
'body': f'Job "{job.title}" restarted'
|
||||
}
|
72
app/blueprints/jobs/json_routes.py
Normal file
72
app/blueprints/jobs/json_routes.py
Normal file
@ -0,0 +1,72 @@
|
||||
from flask import abort, current_app
|
||||
from flask_login import current_user
|
||||
from threading import Thread
|
||||
from app import db
|
||||
from app.decorators import admin_required, content_negotiation
|
||||
from app.models import Job, JobStatus
|
||||
from . import bp
|
||||
|
||||
|
||||
@bp.route('/<hashid:job_id>', methods=['DELETE'])
|
||||
@content_negotiation(produces='application/json')
|
||||
def delete_job(job_id):
|
||||
def _delete_job(app, job_id):
|
||||
with app.app_context():
|
||||
job = Job.query.get(job_id)
|
||||
job.delete()
|
||||
db.session.commit()
|
||||
|
||||
job = Job.query.get_or_404(job_id)
|
||||
if not (job.user == current_user or current_user.is_administrator):
|
||||
abort(403)
|
||||
thread = Thread(
|
||||
target=_delete_job,
|
||||
args=(current_app._get_current_object(), job_id)
|
||||
)
|
||||
thread.start()
|
||||
response_data = {
|
||||
'message': f'Job "{job.title}" marked for deletion'
|
||||
}
|
||||
return response_data, 202
|
||||
|
||||
|
||||
@bp.route('/<hashid:job_id>/log')
|
||||
@admin_required
|
||||
@content_negotiation(produces='application/json')
|
||||
def job_log(job_id):
|
||||
job = Job.query.get_or_404(job_id)
|
||||
if job.status not in [JobStatus.COMPLETED, JobStatus.FAILED]:
|
||||
response = {'errors': {'message': 'Job status is not completed or failed'}}
|
||||
return response, 409
|
||||
with open(job.path / 'pipeline_data' / 'logs' / 'pyflow_log.txt') as log_file:
|
||||
log = log_file.read()
|
||||
response_data = {
|
||||
'jobLog': log
|
||||
}
|
||||
return response_data, 200
|
||||
|
||||
|
||||
@bp.route('/<hashid:job_id>/restart', methods=['POST'])
|
||||
@content_negotiation(produces='application/json')
|
||||
def restart_job(job_id):
|
||||
def _restart_job(app, job_id):
|
||||
with app.app_context():
|
||||
job = Job.query.get(job_id)
|
||||
job.restart()
|
||||
db.session.commit()
|
||||
|
||||
job = Job.query.get_or_404(job_id)
|
||||
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"'}}
|
||||
return response, 409
|
||||
thread = Thread(
|
||||
target=_restart_job,
|
||||
args=(current_app._get_current_object(), job_id)
|
||||
)
|
||||
thread.start()
|
||||
response_data = {
|
||||
'message': f'Job "{job.title}" marked for restarting'
|
||||
}
|
||||
return response_data, 202
|
55
app/blueprints/jobs/routes.py
Normal file
55
app/blueprints/jobs/routes.py
Normal file
@ -0,0 +1,55 @@
|
||||
from flask import (
|
||||
abort,
|
||||
redirect,
|
||||
render_template,
|
||||
send_from_directory,
|
||||
url_for
|
||||
)
|
||||
from flask_login import current_user
|
||||
from app.models import Job, JobInput, JobResult
|
||||
from . import bp
|
||||
|
||||
|
||||
@bp.route('')
|
||||
def jobs():
|
||||
return redirect(url_for('main.dashboard', _anchor='jobs'))
|
||||
|
||||
|
||||
@bp.route('/<hashid:job_id>')
|
||||
def job(job_id):
|
||||
job = Job.query.get_or_404(job_id)
|
||||
if not (job.user == current_user or current_user.is_administrator):
|
||||
abort(403)
|
||||
return render_template(
|
||||
'jobs/job.html.j2',
|
||||
title='Job',
|
||||
job=job
|
||||
)
|
||||
|
||||
|
||||
@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):
|
||||
abort(403)
|
||||
return send_from_directory(
|
||||
job_input.path.parent,
|
||||
job_input.path.name,
|
||||
as_attachment=True,
|
||||
download_name=job_input.filename,
|
||||
mimetype=job_input.mimetype
|
||||
)
|
||||
|
||||
|
||||
@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):
|
||||
abort(403)
|
||||
return send_from_directory(
|
||||
job_result.path.parent,
|
||||
job_result.path.name,
|
||||
as_attachment=True,
|
||||
download_name=job_result.filename,
|
||||
mimetype=job_result.mimetype
|
||||
)
|
Reference in New Issue
Block a user