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