mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-11-15 01:05:42 +00:00
rework jobs package 1/2
This commit is contained in:
parent
5f27ce2801
commit
d6fcaa97bf
@ -25,11 +25,11 @@ def delete_tesseract_model(tesseract_ocr_pipeline_model_id):
|
|||||||
args=(current_app._get_current_object(), topm.id)
|
args=(current_app._get_current_object(), topm.id)
|
||||||
)
|
)
|
||||||
thread.start()
|
thread.start()
|
||||||
resonse_data = {
|
response_data = {
|
||||||
'message': \
|
'message': \
|
||||||
f'Tesseract OCR Pipeline Model "{topm.title}" marked for deletion'
|
f'Tesseract OCR Pipeline Model "{topm.title}" marked for deletion'
|
||||||
}
|
}
|
||||||
response = jsonify(resonse_data)
|
response = jsonify(response_data)
|
||||||
response.status_code = 202
|
response.status_code = 202
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -39,6 +39,8 @@ def delete_tesseract_model(tesseract_ocr_pipeline_model_id):
|
|||||||
@permission_required('CONTRIBUTE')
|
@permission_required('CONTRIBUTE')
|
||||||
@content_negotiation(consumes='application/json', produces='application/json')
|
@content_negotiation(consumes='application/json', produces='application/json')
|
||||||
def update_tesseract_ocr_pipeline_model_is_public(tesseract_ocr_pipeline_model_id):
|
def update_tesseract_ocr_pipeline_model_is_public(tesseract_ocr_pipeline_model_id):
|
||||||
|
# body: jsonify({'is_public': True})
|
||||||
|
# body: jsonify(False)
|
||||||
is_public = request.json
|
is_public = request.json
|
||||||
if not isinstance(is_public, bool):
|
if not isinstance(is_public, bool):
|
||||||
abort(400)
|
abort(400)
|
||||||
|
@ -2,4 +2,4 @@ from flask import Blueprint
|
|||||||
|
|
||||||
|
|
||||||
bp = Blueprint('jobs', __name__)
|
bp = Blueprint('jobs', __name__)
|
||||||
from . import routes
|
from . import routes, json_routes
|
||||||
|
69
app/jobs/json_routes.py
Normal file
69
app/jobs/json_routes.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
from flask import (abort, current_app, jsonify)
|
||||||
|
from flask_login import current_user, login_required
|
||||||
|
from threading import Thread
|
||||||
|
import os
|
||||||
|
from app import db
|
||||||
|
from app.decorators import admin_required, content_negotiation
|
||||||
|
from app.models import Job, JobInput, JobResult, JobStatus
|
||||||
|
from . import bp
|
||||||
|
|
||||||
|
@bp.route('/<hashid:job_id>', methods=['DELETE'])
|
||||||
|
@login_required
|
||||||
|
@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'
|
||||||
|
}
|
||||||
|
response = jsonify(response_data)
|
||||||
|
response.status_code = 202
|
||||||
|
return response
|
||||||
|
|
||||||
|
@bp.route('/<hashid:job_id>/log')
|
||||||
|
@login_required
|
||||||
|
@admin_required
|
||||||
|
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(os.path.join(job.path, 'pipeline_data', 'logs', 'pyflow_log.txt')) as log_file:
|
||||||
|
log = log_file.read()
|
||||||
|
return log, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/<hashid:job_id>/restart', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
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()
|
||||||
|
return {}, 202
|
@ -26,62 +26,6 @@ def job(job_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<hashid:job_id>', methods=['DELETE'])
|
|
||||||
@login_required
|
|
||||||
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()
|
|
||||||
return {}, 202
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<hashid:job_id>/log')
|
|
||||||
@login_required
|
|
||||||
@admin_required
|
|
||||||
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(os.path.join(job.path, 'pipeline_data', 'logs', 'pyflow_log.txt')) as log_file:
|
|
||||||
log = log_file.read()
|
|
||||||
return log, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<hashid:job_id>/restart', methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
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()
|
|
||||||
return {}, 202
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<hashid:job_id>/inputs/<hashid:job_input_id>/download')
|
@bp.route('/<hashid:job_id>/inputs/<hashid:job_input_id>/download')
|
||||||
@login_required
|
@login_required
|
||||||
def download_job_input(job_id, job_input_id):
|
def download_job_input(job_id, job_input_id):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Requests = {};
|
let Requests = {};
|
||||||
|
|
||||||
Requests.JSONfetch = (input, init={}) => {
|
Requests.JSONfetch = (input, init={}) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
23
app/static/js/Requests/jobs/jobs.js
Normal file
23
app/static/js/Requests/jobs/jobs.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Jobs *
|
||||||
|
* Fetch requests for /jobs routes *
|
||||||
|
*****************************************************************************/
|
||||||
|
Requests.jobs = {};
|
||||||
|
|
||||||
|
Requests.jobs.entity = {};
|
||||||
|
|
||||||
|
Requests.jobs.entity.delete = (jobId) => {
|
||||||
|
let input = `/jobs/${jobId}`;
|
||||||
|
let init = {
|
||||||
|
method: 'DELETE'
|
||||||
|
};
|
||||||
|
return Requests.JSONfetch(input, init);
|
||||||
|
}
|
||||||
|
|
||||||
|
Requests.jobs.entity.log = (jobId) => {
|
||||||
|
let input = `/jobs/${jobId}/log`;
|
||||||
|
let init = {
|
||||||
|
method: 'GET'
|
||||||
|
};
|
||||||
|
return Requests.JSONfetch(input, init);
|
||||||
|
}
|
@ -2,11 +2,6 @@ class JobDisplay extends ResourceDisplay {
|
|||||||
constructor(displayElement) {
|
constructor(displayElement) {
|
||||||
super(displayElement);
|
super(displayElement);
|
||||||
this.jobId = this.displayElement.dataset.jobId;
|
this.jobId = this.displayElement.dataset.jobId;
|
||||||
this.displayElement
|
|
||||||
.querySelector('.action-button[data-action="delete-request"]')
|
|
||||||
.addEventListener('click', (event) => {
|
|
||||||
Utils.deleteJobRequest(this.userId, this.jobId);
|
|
||||||
});
|
|
||||||
this.displayElement
|
this.displayElement
|
||||||
.querySelector('.action-button[data-action="get-log-request"]')
|
.querySelector('.action-button[data-action="get-log-request"]')
|
||||||
.addEventListener('click', (event) => {
|
.addEventListener('click', (event) => {
|
||||||
|
@ -493,32 +493,6 @@ class Utils {
|
|||||||
job = {};
|
job = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
let modalElement = Utils.HTMLToElement(
|
|
||||||
`
|
|
||||||
<div class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<h4>Confirm Job deletion</h4>
|
|
||||||
<p>Do you really want to delete the Job <b>${job?.title}</b>? All files will be permanently deleted!</p>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<a class="action-button btn modal-close waves-effect waves-light" data-action="cancel">Cancel</a>
|
|
||||||
<a class="action-button btn modal-close red waves-effect waves-light" data-action="confirm">Delete</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
);
|
|
||||||
document.querySelector('#modals').appendChild(modalElement);
|
|
||||||
let modal = M.Modal.init(
|
|
||||||
modalElement,
|
|
||||||
{
|
|
||||||
dismissible: false,
|
|
||||||
onCloseEnd: () => {
|
|
||||||
modal.destroy();
|
|
||||||
modalElement.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
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) => {
|
||||||
let jobTitle = job?.title;
|
let jobTitle = job?.title;
|
||||||
|
@ -61,7 +61,8 @@
|
|||||||
'js/Requests/contributions/contributions.js',
|
'js/Requests/contributions/contributions.js',
|
||||||
'js/Requests/contributions/spacy_nlp_pipeline_models.js',
|
'js/Requests/contributions/spacy_nlp_pipeline_models.js',
|
||||||
'js/Requests/contributions/tesseract_ocr_pipeline_models.js',
|
'js/Requests/contributions/tesseract_ocr_pipeline_models.js',
|
||||||
'js/Requests/Corpora.js'
|
'js/Requests/Corpora.js',
|
||||||
|
'js/Requests/jobs/jobs.js'
|
||||||
%}
|
%}
|
||||||
<script src="{{ ASSET_URL }}"></script>
|
<script src="{{ ASSET_URL }}"></script>
|
||||||
{%- endassets %}
|
{%- endassets %}
|
||||||
|
@ -79,10 +79,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-action right-align">
|
<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" data-action="get-log-request"><i class="material-icons left">text_snippet</i>Log</a>
|
<a class="btn disabled waves-effect waves-light modal-trigger" id="log-job-modal"><i class="material-icons left">text_snippet</i>Log</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="action-button btn disabled waves-effect waves-light" data-action="restart-request"><i class="material-icons left">repeat</i>Restart</a>
|
<a class="action-button btn disabled waves-effect waves-light" data-action="restart-request"><i class="material-icons left">repeat</i>Restart</a>
|
||||||
<a class="action-button btn red waves-effect waves-light" data-action="delete-request"><i class="material-icons left">delete</i>Delete</a>
|
<a class="btn red waves-effect waves-light modal-trigger" href="#delete-job-modal"><i class="material-icons left">delete</i>Delete</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -112,10 +112,42 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endblock page_content %}
|
{% endblock page_content %}
|
||||||
|
|
||||||
|
{% block modals %}
|
||||||
|
{{ super() }}
|
||||||
|
<div class="modal" id="delete-job-modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h4>Confirm Job deletion</h4>
|
||||||
|
<p>Do you really want to delete the Job <b>{{job.title}}</b>? All files will be permanently deleted!</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a class="action-button btn modal-close waves-effect waves-light" data-action="cancel">Cancel</a>
|
||||||
|
<a class="btn modal-close red waves-effect waves-light" id="delete-job-request">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal" id="log-job-modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h4>Job logs</h4>
|
||||||
|
<pre><code>${text}</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a class="btn modal-close waves-effect waves-light">Close</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock modals %}
|
||||||
|
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script>
|
<script>
|
||||||
let jobDisplay = new JobDisplay(document.querySelector('#job-display'));
|
let jobDisplay = new JobDisplay(document.querySelector('#job-display'));
|
||||||
|
let deleteJobRequestElement = document.querySelector('#delete-job-request');
|
||||||
|
let logJobModalElement = document.querySelector('#log-job-modal');
|
||||||
|
deleteJobRequestElement.addEventListener('click', (event) => {
|
||||||
|
Requests.jobs.entity.delete({{ job.hashid|tojson }});
|
||||||
|
});
|
||||||
|
logJobModalElement.addEventListener('click', (event) => {
|
||||||
|
Requests.jobs.entity.log({{ job.hashid|tojson }});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock scripts %}
|
{% endblock scripts %}
|
||||||
|
Loading…
Reference in New Issue
Block a user