mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-12-25 10:54:18 +00:00
Add job namespace and remove old json_routes logic
This commit is contained in:
parent
1372c86609
commit
93344c9573
@ -133,6 +133,9 @@ def create_app(config: Config = Config) -> Flask:
|
||||
from .namespaces.cqi_over_sio import CQiOverSocketIONamespace
|
||||
socketio.on_namespace(CQiOverSocketIONamespace('/cqi_over_sio'))
|
||||
|
||||
from .namespaces.jobs import JobsNamespace
|
||||
socketio.on_namespace(JobsNamespace('/jobs'))
|
||||
|
||||
from .namespaces.users import UsersNamespace
|
||||
socketio.on_namespace(UsersNamespace('/users'))
|
||||
# endregion SocketIO Namespaces
|
||||
|
@ -15,4 +15,4 @@ def before_request():
|
||||
pass
|
||||
|
||||
|
||||
from . import routes, json_routes
|
||||
from . import routes
|
||||
|
@ -1,72 +0,0 @@
|
||||
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
|
@ -13,14 +13,14 @@ def _delete_job(app: Flask, job_id: int):
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def _restart_job(app, job_id):
|
||||
def _restart_job(app: Flask, job_id: int):
|
||||
with app.app_context():
|
||||
job = Job.query.get(job_id)
|
||||
job.restart()
|
||||
db.session.commit()
|
||||
|
||||
|
||||
class UsersNamespace(Namespace):
|
||||
class JobsNamespace(Namespace):
|
||||
@socketio_login_required
|
||||
def on_delete(self, job_hashid: str) -> dict:
|
||||
job_id = hashids.decode(job_hashid)
|
||||
@ -52,7 +52,7 @@ class UsersNamespace(Namespace):
|
||||
}
|
||||
|
||||
@socketio_admin_required
|
||||
def on_log(self, job_hashid: str):
|
||||
def on_log(self, job_hashid: str) -> dict:
|
||||
job_id = hashids.decode(job_hashid)
|
||||
|
||||
if not isinstance(job_id, int):
|
||||
@ -76,7 +76,7 @@ class UsersNamespace(Namespace):
|
||||
}
|
||||
|
||||
socketio_login_required
|
||||
def on_restart(self, job_hashid: str):
|
||||
def on_restart(self, job_hashid: str) -> dict:
|
||||
job_id = hashids.decode(job_hashid)
|
||||
|
||||
if not isinstance(job_id, int):
|
||||
|
@ -3,6 +3,7 @@ nopaque.App = class App {
|
||||
this.socket = io({transports: ['websocket'], upgrade: false});
|
||||
|
||||
// Endpoints
|
||||
this.jobs = new nopaque.app.endpoints.Jobs(this);
|
||||
this.users = new nopaque.app.endpoints.Users(this);
|
||||
|
||||
// Extensions
|
||||
|
37
app/static/js/app/endpoints/jobs.js
Normal file
37
app/static/js/app/endpoints/jobs.js
Normal file
@ -0,0 +1,37 @@
|
||||
nopaque.app.endpoints.Jobs = class Jobs {
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
|
||||
this.socket = io('/jobs', {transports: ['websocket'], upgrade: false});
|
||||
}
|
||||
|
||||
async delete(id) {
|
||||
const response = await this.socket.emitWithAck('delete', id);
|
||||
|
||||
if (response.status != 202) {
|
||||
throw new Error(`[${response.status}] ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.body;
|
||||
}
|
||||
|
||||
async log(id) {
|
||||
const response = await this.socket.emitWithAck('log', id);
|
||||
|
||||
if (response.status != 200) {
|
||||
throw new Error(`[${response.status}] ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.body;
|
||||
}
|
||||
|
||||
async restart(id) {
|
||||
const response = await this.socket.emitWithAck('restart', id);
|
||||
|
||||
if (response.status != 202) {
|
||||
throw new Error(`[${response.status}] ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.body;
|
||||
}
|
||||
}
|
@ -5,8 +5,8 @@ nopaque.app.endpoints.Users = class Users {
|
||||
this.socket = io('/users', {transports: ['websocket'], upgrade: false});
|
||||
}
|
||||
|
||||
async get(userId) {
|
||||
const response = await this.socket.emitWithAck('get', userId);
|
||||
async get(id) {
|
||||
const response = await this.socket.emitWithAck('get', id);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`[${response.status}] ${response.statusText}`);
|
||||
@ -15,27 +15,29 @@ nopaque.app.endpoints.Users = class Users {
|
||||
return response.body;
|
||||
}
|
||||
|
||||
async subscribe(userId) {
|
||||
const response = await this.socket.emitWithAck('subscribe', userId);
|
||||
async subscribe(id) {
|
||||
const response = await this.socket.emitWithAck('subscribe', id);
|
||||
|
||||
if (response.status != 200) {
|
||||
throw new Error(`[${response.status}] ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
async unsubscribe(userId) {
|
||||
const response = await this.socket.emitWithAck('unsubscribe', userId);
|
||||
async unsubscribe(id) {
|
||||
const response = await this.socket.emitWithAck('unsubscribe', id);
|
||||
|
||||
if (response.status != 200) {
|
||||
throw new Error(`[${response.status}] ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
async delete(userId) {
|
||||
const response = await this.socket.emitWithAck('delete', userId);
|
||||
async delete(id) {
|
||||
const response = await this.socket.emitWithAck('delete', id);
|
||||
|
||||
if (response.status != 202) {
|
||||
throw new Error(`[${response.status}] ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.body;
|
||||
}
|
||||
}
|
||||
|
@ -136,8 +136,9 @@ nopaque.resource_lists.JobList = class JobList extends nopaque.resource_lists.Re
|
||||
}
|
||||
);
|
||||
let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]');
|
||||
confirmElement.addEventListener('click', (event) => {
|
||||
nopaque.requests.jobs.entity.delete(itemId);
|
||||
confirmElement.addEventListener('click', async (event) => {
|
||||
const message = await app.jobs.delete(itemId);
|
||||
app.ui.flash(message, 'job');
|
||||
});
|
||||
modal.open();
|
||||
break;
|
||||
@ -221,8 +222,9 @@ nopaque.resource_lists.JobList = class JobList extends nopaque.resource_lists.Re
|
||||
);
|
||||
let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]');
|
||||
confirmElement.addEventListener('click', (event) => {
|
||||
this.selectedItemIds.forEach(selectedItemId => {
|
||||
nopaque.requests.jobs.entity.delete(selectedItemId);
|
||||
this.selectedItemIds.forEach(async (selectedItemId) => {
|
||||
const message = await app.jobs.delete(selectedItemId);
|
||||
app.ui.flash(message, 'job');
|
||||
});
|
||||
this.selectedItemIds.clear();
|
||||
this.renderingItemSelection();
|
||||
|
@ -11,6 +11,7 @@
|
||||
'js/app.js',
|
||||
'js/app/index.js',
|
||||
'js/app/endpoints/index.js',
|
||||
'js/app/endpoints/jobs.js',
|
||||
'js/app/endpoints/users.js',
|
||||
'js/app/extensions/index.js',
|
||||
'js/app/extensions/toaster.js',
|
||||
|
@ -137,28 +137,26 @@
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
let deleteJobRequestElement = document.querySelector('#delete-job-request');
|
||||
let restartJobRequestElement = document.querySelector('#restart-job-request');
|
||||
deleteJobRequestElement.addEventListener('click', (event) => {
|
||||
nopaque.requests.jobs.entity.delete({{ job.hashid|tojson }});
|
||||
const deleteJobRequestElement = document.querySelector('#delete-job-request');
|
||||
const restartJobRequestElement = document.querySelector('#restart-job-request');
|
||||
|
||||
deleteJobRequestElement.addEventListener('click', async (event) => {
|
||||
const message = await app.jobs.delete({{ job.hashid|tojson }});
|
||||
app.ui.flash(message, 'job');
|
||||
});
|
||||
restartJobRequestElement.addEventListener('click', (event) => {
|
||||
nopaque.requests.jobs.entity.restart({{ job.hashid|tojson }});
|
||||
restartJobRequestElement.addEventListener('click', async (event) => {
|
||||
const message = await app.jobs.restart({{ job.hashid|tojson }});
|
||||
app.ui.flash(message, 'job');
|
||||
});
|
||||
|
||||
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 }})
|
||||
.then(
|
||||
(response) => {
|
||||
response.json()
|
||||
.then((json) => {
|
||||
let jobLogModalElement = document.querySelector('#job-log-modal');
|
||||
jobLogModalElement.querySelector('pre code').textContent = json.jobLog;
|
||||
{% if current_user.is_administrator %}
|
||||
const jobLogButtonElement = document.querySelector('#job-log-button');
|
||||
|
||||
jobLogButtonElement.addEventListener('click', async (event) => {
|
||||
const jobLogModalElement = document.querySelector('#job-log-modal');
|
||||
const log = await app.jobs.log({{ job.hashid|tojson }});
|
||||
jobLogModalElement.querySelector('pre code').textContent = log;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock scripts %}
|
||||
|
Loading…
Reference in New Issue
Block a user