mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2025-07-06 20:53:18 +00:00
Compare commits
3 Commits
access-pip
...
cdf6f9fcfd
Author | SHA1 | Date | |
---|---|---|---|
cdf6f9fcfd | |||
268da220d2 | |||
84e1755a57 |
@ -5,9 +5,8 @@
|
|||||||
!app
|
!app
|
||||||
!migrations
|
!migrations
|
||||||
!tests
|
!tests
|
||||||
!.flaskenv
|
|
||||||
!boot.sh
|
!boot.sh
|
||||||
!config.py
|
!config.py
|
||||||
!docker-nopaque-entrypoint.sh
|
!docker-nopaque-entrypoint.sh
|
||||||
!nopaque.py
|
|
||||||
!requirements.txt
|
!requirements.txt
|
||||||
|
!wsgi.py
|
||||||
|
@ -46,7 +46,7 @@ COPY docker-nopaque-entrypoint.sh /usr/local/bin/
|
|||||||
COPY --chown=nopaque:nopaque app app
|
COPY --chown=nopaque:nopaque app app
|
||||||
COPY --chown=nopaque:nopaque migrations migrations
|
COPY --chown=nopaque:nopaque migrations migrations
|
||||||
COPY --chown=nopaque:nopaque tests tests
|
COPY --chown=nopaque:nopaque tests tests
|
||||||
COPY --chown=nopaque:nopaque .flaskenv boot.sh config.py nopaque.py requirements.txt ./
|
COPY --chown=nopaque:nopaque boot.sh config.py wsgi.py requirements.txt ./
|
||||||
|
|
||||||
RUN mkdir logs
|
RUN mkdir logs
|
||||||
|
|
||||||
|
@ -33,6 +33,9 @@ scheduler = APScheduler()
|
|||||||
socketio = SocketIO()
|
socketio = SocketIO()
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Create export for lemmatized corpora
|
||||||
|
|
||||||
|
|
||||||
def create_app(config: Config = Config) -> Flask:
|
def create_app(config: Config = Config) -> Flask:
|
||||||
''' Creates an initialized Flask (WSGI Application) object. '''
|
''' Creates an initialized Flask (WSGI Application) object. '''
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from flask import abort, request
|
from flask import abort, request
|
||||||
from app import db
|
|
||||||
from app.decorators import content_negotiation
|
from app.decorators import content_negotiation
|
||||||
|
from app import db
|
||||||
from app.models import User
|
from app.models import User
|
||||||
from . import bp
|
from . import bp
|
||||||
|
|
||||||
|
@ -9,12 +9,12 @@ from app.users.settings.forms import (
|
|||||||
UpdateAccountInformationForm,
|
UpdateAccountInformationForm,
|
||||||
UpdateProfileInformationForm
|
UpdateProfileInformationForm
|
||||||
)
|
)
|
||||||
from . import bp
|
|
||||||
from .forms import UpdateUserForm
|
|
||||||
from app.users.utils import (
|
from app.users.utils import (
|
||||||
user_endpoint_arguments_constructor as user_eac,
|
user_endpoint_arguments_constructor as user_eac,
|
||||||
user_dynamic_list_constructor as user_dlc
|
user_dynamic_list_constructor as user_dlc
|
||||||
)
|
)
|
||||||
|
from . import bp
|
||||||
|
from .forms import UpdateUserForm
|
||||||
|
|
||||||
|
|
||||||
@bp.route('')
|
@bp.route('')
|
||||||
|
@ -5,8 +5,8 @@ from flask import abort, Blueprint
|
|||||||
from werkzeug.exceptions import InternalServerError
|
from werkzeug.exceptions import InternalServerError
|
||||||
from app import db, hashids
|
from app import db, hashids
|
||||||
from app.models import Job, JobInput, JobStatus, TesseractOCRPipelineModel
|
from app.models import Job, JobInput, JobStatus, TesseractOCRPipelineModel
|
||||||
from .schemas import EmptySchema, JobSchema, SpaCyNLPPipelineJobSchema, TesseractOCRPipelineJobSchema, TesseractOCRPipelineModelSchema
|
|
||||||
from .auth import auth_error_responses, token_auth
|
from .auth import auth_error_responses, token_auth
|
||||||
|
from .schemas import EmptySchema, JobSchema, SpaCyNLPPipelineJobSchema, TesseractOCRPipelineJobSchema, TesseractOCRPipelineModelSchema
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint('jobs', __name__)
|
bp = Blueprint('jobs', __name__)
|
||||||
|
@ -3,11 +3,11 @@ from apifairy import authenticate, body, response
|
|||||||
from apifairy.decorators import other_responses
|
from apifairy.decorators import other_responses
|
||||||
from flask import abort, Blueprint
|
from flask import abort, Blueprint
|
||||||
from werkzeug.exceptions import InternalServerError
|
from werkzeug.exceptions import InternalServerError
|
||||||
from app import db
|
|
||||||
from app.email import create_message, send
|
from app.email import create_message, send
|
||||||
|
from app import db
|
||||||
from app.models import User
|
from app.models import User
|
||||||
from .schemas import EmptySchema, UserSchema
|
|
||||||
from .auth import auth_error_responses, token_auth
|
from .auth import auth_error_responses, token_auth
|
||||||
|
from .schemas import EmptySchema, UserSchema
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint('users', __name__)
|
bp = Blueprint('users', __name__)
|
||||||
|
@ -4,7 +4,7 @@ from threading import Thread
|
|||||||
from app import db
|
from app import db
|
||||||
from app.decorators import content_negotiation, permission_required
|
from app.decorators import content_negotiation, permission_required
|
||||||
from app.models import SpaCyNLPPipelineModel
|
from app.models import SpaCyNLPPipelineModel
|
||||||
from .. import bp
|
from . import bp
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/spacy-nlp-pipeline-models/<hashid:spacy_nlp_pipeline_model_id>', methods=['DELETE'])
|
@bp.route('/spacy-nlp-pipeline-models/<hashid:spacy_nlp_pipeline_model_id>', methods=['DELETE'])
|
||||||
|
@ -8,9 +8,7 @@ from .forms import (
|
|||||||
CreateSpaCyNLPPipelineModelForm,
|
CreateSpaCyNLPPipelineModelForm,
|
||||||
UpdateSpaCyNLPPipelineModelForm
|
UpdateSpaCyNLPPipelineModelForm
|
||||||
)
|
)
|
||||||
from .utils import (
|
from .utils import spacy_nlp_pipeline_model_dlc
|
||||||
spacy_nlp_pipeline_model_dlc as spacy_nlp_pipeline_model_dlc
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/spacy-nlp-pipeline-models')
|
@bp.route('/spacy-nlp-pipeline-models')
|
||||||
|
@ -8,9 +8,7 @@ from .forms import (
|
|||||||
CreateTesseractOCRPipelineModelForm,
|
CreateTesseractOCRPipelineModelForm,
|
||||||
UpdateTesseractOCRPipelineModelForm
|
UpdateTesseractOCRPipelineModelForm
|
||||||
)
|
)
|
||||||
from .utils import (
|
from .utils import tesseract_ocr_pipeline_model_dlc
|
||||||
tesseract_ocr_pipeline_model_dlc as tesseract_ocr_pipeline_model_dlc
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/tesseract-ocr-pipeline-models')
|
@bp.route('/tesseract-ocr-pipeline-models')
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from flask import current_app
|
|
||||||
from app import db
|
|
||||||
from app.models import User, Corpus, CorpusFile
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from flask import current_app
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
|
from app import db
|
||||||
|
from app.models import User, Corpus, CorpusFile
|
||||||
|
|
||||||
|
|
||||||
class SandpaperConverter:
|
class SandpaperConverter:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from flask import abort, current_app
|
from flask import current_app
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from app import db
|
|
||||||
from app.decorators import content_negotiation
|
from app.decorators import content_negotiation
|
||||||
|
from app import db
|
||||||
from app.models import CorpusFile
|
from app.models import CorpusFile
|
||||||
from ..decorators import corpus_follower_permission_required
|
from ..decorators import corpus_follower_permission_required
|
||||||
from . import bp
|
from . import bp
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from flask import request, url_for
|
from flask import request, url_for
|
||||||
from app.models import CorpusFile
|
from app.models import CorpusFile
|
||||||
from ..utils import corpus_endpoint_arguments_constructor as corpus_eac
|
|
||||||
|
|
||||||
|
|
||||||
def corpus_file_dynamic_list_constructor():
|
def corpus_file_dynamic_list_constructor():
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
from .container_column import ContainerColumn
|
|
||||||
from .int_enum_column import IntEnumColumn
|
|
@ -1,21 +0,0 @@
|
|||||||
import json
|
|
||||||
from app import db
|
|
||||||
|
|
||||||
|
|
||||||
class ContainerColumn(db.TypeDecorator):
|
|
||||||
impl = db.String
|
|
||||||
|
|
||||||
def __init__(self, container_type, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.container_type = container_type
|
|
||||||
|
|
||||||
def process_bind_param(self, value, dialect):
|
|
||||||
if isinstance(value, self.container_type):
|
|
||||||
return json.dumps(value)
|
|
||||||
elif isinstance(value, str) and isinstance(json.loads(value), self.container_type):
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
return TypeError()
|
|
||||||
|
|
||||||
def process_result_value(self, value, dialect):
|
|
||||||
return json.loads(value)
|
|
1
app/extensions/__init__.py
Normal file
1
app/extensions/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
2
app/extensions/sqlalchemy/__init__.py
Normal file
2
app/extensions/sqlalchemy/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .types import ContainerColumn
|
||||||
|
from .types import IntEnumColumn
|
@ -1,6 +1,26 @@
|
|||||||
|
import json
|
||||||
from app import db
|
from app import db
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerColumn(db.TypeDecorator):
|
||||||
|
impl = db.String
|
||||||
|
|
||||||
|
def __init__(self, container_type, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.container_type = container_type
|
||||||
|
|
||||||
|
def process_bind_param(self, value, dialect):
|
||||||
|
if isinstance(value, self.container_type):
|
||||||
|
return json.dumps(value)
|
||||||
|
elif isinstance(value, str) and isinstance(json.loads(value), self.container_type):
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return TypeError()
|
||||||
|
|
||||||
|
def process_result_value(self, value, dialect):
|
||||||
|
return json.loads(value)
|
||||||
|
|
||||||
|
|
||||||
class IntEnumColumn(db.TypeDecorator):
|
class IntEnumColumn(db.TypeDecorator):
|
||||||
impl = db.Integer
|
impl = db.Integer
|
||||||
|
|
@ -9,7 +9,7 @@ import shutil
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from app import db
|
from app import db
|
||||||
from app.converters.vrt import normalize_vrt_file
|
from app.converters.vrt import normalize_vrt_file
|
||||||
from app.ext.flask_sqlalchemy import IntEnumColumn
|
from app.extensions.sqlalchemy import IntEnumColumn
|
||||||
from .corpus_follower_association import CorpusFollowerAssociation
|
from .corpus_follower_association import CorpusFollowerAssociation
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ from typing import Union
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import shutil
|
import shutil
|
||||||
from app import db
|
from app import db
|
||||||
from app.ext.flask_sqlalchemy import ContainerColumn, IntEnumColumn
|
from app.extensions.sqlalchemy import ContainerColumn, IntEnumColumn
|
||||||
|
|
||||||
|
|
||||||
class JobStatus(IntEnum):
|
class JobStatus(IntEnum):
|
||||||
|
@ -5,7 +5,7 @@ from pathlib import Path
|
|||||||
import requests
|
import requests
|
||||||
import yaml
|
import yaml
|
||||||
from app import db
|
from app import db
|
||||||
from app.ext.flask_sqlalchemy import ContainerColumn
|
from app.extensions.sqlalchemy import ContainerColumn
|
||||||
from .file_mixin import FileMixin
|
from .file_mixin import FileMixin
|
||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from pathlib import Path
|
|||||||
import requests
|
import requests
|
||||||
import yaml
|
import yaml
|
||||||
from app import db
|
from app import db
|
||||||
from app.ext.flask_sqlalchemy import ContainerColumn
|
from app.extensions.sqlalchemy import ContainerColumn
|
||||||
from .file_mixin import FileMixin
|
from .file_mixin import FileMixin
|
||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import re
|
|||||||
import secrets
|
import secrets
|
||||||
import shutil
|
import shutil
|
||||||
from app import db, hashids
|
from app import db, hashids
|
||||||
from app.ext.flask_sqlalchemy import IntEnumColumn
|
from app.extensions.sqlalchemy import IntEnumColumn
|
||||||
from .corpus import Corpus
|
from .corpus import Corpus
|
||||||
from .corpus_follower_association import CorpusFollowerAssociation
|
from .corpus_follower_association import CorpusFollowerAssociation
|
||||||
from .corpus_follower_role import CorpusFollowerRole
|
from .corpus_follower_role import CorpusFollowerRole
|
||||||
|
@ -6,7 +6,6 @@ from app import db, hashids
|
|||||||
from app.models import (
|
from app.models import (
|
||||||
Job,
|
Job,
|
||||||
JobInput,
|
JobInput,
|
||||||
JobResult,
|
|
||||||
JobStatus,
|
JobStatus,
|
||||||
TesseractOCRPipelineModel,
|
TesseractOCRPipelineModel,
|
||||||
SpaCyNLPPipelineModel
|
SpaCyNLPPipelineModel
|
||||||
@ -75,8 +74,6 @@ def tesseract_ocr_pipeline():
|
|||||||
version = request.args.get('version', service_manifest['latest_version'])
|
version = request.args.get('version', service_manifest['latest_version'])
|
||||||
if version not in service_manifest['versions']:
|
if version not in service_manifest['versions']:
|
||||||
abort(404)
|
abort(404)
|
||||||
job_results = JobResult.query.all()
|
|
||||||
choosable_job_ids = [job_result.job.hashid for job_result in job_results if job_result.job.service == "file-setup-pipeline" and job_result.filename.endswith('.pdf')]
|
|
||||||
form = CreateTesseractOCRPipelineJobForm(prefix='create-job-form', version=version)
|
form = CreateTesseractOCRPipelineJobForm(prefix='create-job-form', version=version)
|
||||||
if form.is_submitted():
|
if form.is_submitted():
|
||||||
if not form.validate():
|
if not form.validate():
|
||||||
@ -114,7 +111,6 @@ def tesseract_ocr_pipeline():
|
|||||||
return render_template(
|
return render_template(
|
||||||
'services/tesseract_ocr_pipeline.html.j2',
|
'services/tesseract_ocr_pipeline.html.j2',
|
||||||
title=service_manifest['name'],
|
title=service_manifest['name'],
|
||||||
choosable_job_ids=choosable_job_ids,
|
|
||||||
form=form,
|
form=form,
|
||||||
tesseract_ocr_pipeline_models=tesseract_ocr_pipeline_models,
|
tesseract_ocr_pipeline_models=tesseract_ocr_pipeline_models,
|
||||||
user_tesseract_ocr_pipeline_models_count=user_tesseract_ocr_pipeline_models_count
|
user_tesseract_ocr_pipeline_models_count=user_tesseract_ocr_pipeline_models_count
|
||||||
|
57
app/static/js/forms/base-form-new.js
Normal file
57
app/static/js/forms/base-form-new.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
export class BaseForm {
|
||||||
|
constructor(formElement) {
|
||||||
|
this.element = formElement;
|
||||||
|
|
||||||
|
this.element.addEventListener('submit', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.submit();
|
||||||
|
});
|
||||||
|
console.log("UsernamePostFormInitialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
let errorTextElements = this.element
|
||||||
|
.querySelectorAll('.supporting-text[data-supporting-text-type="error"]');
|
||||||
|
for (let errorTextElement of errorTextElements) {errorTextElement.remove();}
|
||||||
|
|
||||||
|
const body = new FormData(this.element);
|
||||||
|
const headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
};
|
||||||
|
const method = this.element.method;
|
||||||
|
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
fetch(this.element.action, {body: body, headers: headers, method: method})
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
console.log("reject", response);
|
||||||
|
reject(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("resolve", response);
|
||||||
|
resolve(response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
fetchPromise
|
||||||
|
.then(
|
||||||
|
(response) => {console.log("Hello from resolve handler");return response.json();},
|
||||||
|
(response) => {
|
||||||
|
console.log("Hello from reject handler 1/2");
|
||||||
|
response.json()
|
||||||
|
.then((errors) => {
|
||||||
|
console.log("Hello from reject handler 2/2");
|
||||||
|
for (let [name, messages] of Object.entries(errors)) {
|
||||||
|
console.log(name, messages);
|
||||||
|
const inputFieldElement = this.element[name].closest('.input-field');
|
||||||
|
for (let message of messages) {
|
||||||
|
const messageHTML = `<span class="supporting-text" data-supporting-text-type="error">${message}</span>`;
|
||||||
|
inputFieldElement.insertAdjacentHTML('beforeend', messageHTML);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
@ -1,137 +0,0 @@
|
|||||||
nopaque.resource_lists.JobOutputList = class JobOutputList extends nopaque.resource_lists.ResourceList {
|
|
||||||
static htmlClass = 'job-output-list';
|
|
||||||
|
|
||||||
constructor(listContainerElement, options = {}) {
|
|
||||||
super(listContainerElement, options);
|
|
||||||
this.listjs.list.addEventListener('click', (event) => {this.onClick(event)});
|
|
||||||
this.isInitialized = false;
|
|
||||||
this.userId = listContainerElement.dataset.userId;
|
|
||||||
this.jobOutput = listContainerElement.dataset.jobOutput;
|
|
||||||
this.jobIds = listContainerElement.dataset.jobIds;
|
|
||||||
if (this.userId === undefined) {return;}
|
|
||||||
app.subscribeUser(this.userId).then((response) => {
|
|
||||||
app.socket.on('PATCH', (patch) => {
|
|
||||||
if (this.isInitialized) {this.onPatch(patch);}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
app.getUser(this.userId).then((user) => {
|
|
||||||
let jobIds = JSON.parse(this.jobIds.replace(/'/g, '"'));
|
|
||||||
let job_results = {};
|
|
||||||
for (let jobId of jobIds) {
|
|
||||||
for (let jobResult of Object.values(user.jobs[jobId].results)) {
|
|
||||||
if (jobResult.mimetype === 'application/pdf') {
|
|
||||||
job_results[jobResult.id] = jobResult;
|
|
||||||
job_results[jobResult.id].description = user.jobs[jobId].description;
|
|
||||||
job_results[jobResult.id].title = user.jobs[jobId].title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.add(Object.values(job_results));
|
|
||||||
this.isInitialized = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get item() {
|
|
||||||
return `
|
|
||||||
<tr class="list-item clickable hoverable">
|
|
||||||
<td><span class="title"></span></td>
|
|
||||||
<td><span class="description"></span></td>
|
|
||||||
<td><span class="filename"></span></td>
|
|
||||||
<td class="right-align">
|
|
||||||
<a class="list-action-trigger btn-flat waves-effect waves-light" data-list-action="add"><i class="material-icons">add</i></a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
`.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
get valueNames() {
|
|
||||||
return [
|
|
||||||
{data: ['id']},
|
|
||||||
{data: ['creation-date']},
|
|
||||||
'title',
|
|
||||||
'description',
|
|
||||||
'filename'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
initListContainerElement() {
|
|
||||||
if (!this.listContainerElement.hasAttribute('id')) {
|
|
||||||
this.listContainerElement.id = nopaque.Utils.generateElementId('job-output-list-');
|
|
||||||
}
|
|
||||||
let listSearchElementId = nopaque.Utils.generateElementId(`${this.listContainerElement.id}-search-`);
|
|
||||||
this.listContainerElement.innerHTML = `
|
|
||||||
<div class="input-field">
|
|
||||||
<i class="material-icons prefix">search</i>
|
|
||||||
<input id="${listSearchElementId}" class="search" type="text"></input>
|
|
||||||
<label for="${listSearchElementId}">Search job output</label>
|
|
||||||
</div>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Title</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Filename</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="list"></tbody>
|
|
||||||
</table>
|
|
||||||
<ul class="pagination"></ul>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapResourceToValue(jobOutput) {
|
|
||||||
console.log(jobOutput);
|
|
||||||
return {
|
|
||||||
'id': jobOutput.id,
|
|
||||||
'creation-date': jobOutput.creationDate,
|
|
||||||
'title': jobOutput.title,
|
|
||||||
'description': jobOutput.description,
|
|
||||||
'filename': jobOutput.filename
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
sort() {
|
|
||||||
this.listjs.sort('title', {order: 'asc'});
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick(event) {
|
|
||||||
let listItemElement = event.target.closest('.list-item[data-id]');
|
|
||||||
if (listItemElement === null) {return;}
|
|
||||||
let itemId = listItemElement.dataset.id;
|
|
||||||
let listActionElement = event.target.closest('.list-action-trigger[data-list-action]');
|
|
||||||
let listAction = listActionElement === null ? 'add' : listActionElement.dataset.listAction;
|
|
||||||
switch (listAction) {
|
|
||||||
case 'add': {
|
|
||||||
listActionElement.querySelector('i').textContent = 'done';
|
|
||||||
listActionElement.dataset.listAction = 'remove';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'remove': {
|
|
||||||
listActionElement.querySelector('i').textContent = 'add';
|
|
||||||
listActionElement.dataset.listAction = 'add';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// onPatch(patch) {
|
|
||||||
// let re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)`);
|
|
||||||
// let filteredPatch = patch.filter(operation => re.test(operation.path));
|
|
||||||
// for (let operation of filteredPatch) {
|
|
||||||
// switch(operation.op) {
|
|
||||||
// case 'add': {
|
|
||||||
// let re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)$`);
|
|
||||||
// if (re.test(operation.path)) {this.add(operation.value);}
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// default: {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
};
|
|
@ -52,7 +52,6 @@
|
|||||||
'js/resource-lists/job-input-list.js',
|
'js/resource-lists/job-input-list.js',
|
||||||
'js/resource-lists/job-list.js',
|
'js/resource-lists/job-list.js',
|
||||||
'js/resource-lists/job-result-list.js',
|
'js/resource-lists/job-result-list.js',
|
||||||
'js/resource-lists/job-output-list.js',
|
|
||||||
'js/resource-lists/public-corpus-list.js',
|
'js/resource-lists/public-corpus-list.js',
|
||||||
'js/resource-lists/public-user-list.js',
|
'js/resource-lists/public-user-list.js',
|
||||||
'js/resource-lists/spacy-nlp-pipeline-model-list.js',
|
'js/resource-lists/spacy-nlp-pipeline-model-list.js',
|
||||||
|
@ -37,15 +37,6 @@
|
|||||||
|
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
<h2>Submit a job</h2>
|
<h2>Submit a job</h2>
|
||||||
<div class="card">
|
|
||||||
<div class="card-content">
|
|
||||||
<p>Add an existing file from your workflow or add a new one below.</p>
|
|
||||||
<div class="job-output-list" data-user-id="{{ current_user.hashid}}" data-job-ids="{{ choosable_job_ids }}"></div>
|
|
||||||
</div>
|
|
||||||
<div class="card-action right-align">
|
|
||||||
<a class="waves-effect waves-light btn"><i class="material-icons right">send</i>Submit</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<form class="create-job-form" enctype="multipart/form-data" method="POST">
|
<form class="create-job-form" enctype="multipart/form-data" method="POST">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
@ -60,8 +51,6 @@
|
|||||||
<div class="col s12 l5">
|
<div class="col s12 l5">
|
||||||
{{ wtf.render_field(form.pdf, accept='application/pdf', placeholder='Choose a PDF file') }}
|
{{ wtf.render_field(form.pdf, accept='application/pdf', placeholder='Choose a PDF file') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s12 l4">
|
<div class="col s12 l4">
|
||||||
<div class="input-field">
|
<div class="input-field">
|
||||||
<i class="material-icons prefix">language</i>
|
<i class="material-icons prefix">language</i>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
from flask_login import current_user
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from flask_wtf.file import FileField, FileRequired
|
from flask_wtf.file import FileField, FileRequired
|
||||||
from wtforms import (
|
from wtforms import (
|
||||||
@ -17,7 +16,7 @@ from wtforms.validators import (
|
|||||||
Regexp
|
Regexp
|
||||||
)
|
)
|
||||||
from app.models import User, UserSettingJobStatusMailNotificationLevel
|
from app.models import User, UserSettingJobStatusMailNotificationLevel
|
||||||
from app.wtforms.validators import FileSize
|
from app.extensions.wtforms.validators import FileSize
|
||||||
|
|
||||||
|
|
||||||
class UpdateAccountInformationForm(FlaskForm):
|
class UpdateAccountInformationForm(FlaskForm):
|
||||||
|
2
boot.sh
2
boot.sh
@ -24,7 +24,7 @@ if [[ "${#}" == "0" ]]; then
|
|||||||
sleep 5
|
sleep 5
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
python3 nopaque.py
|
python3 wsgi.py
|
||||||
elif [[ "${1}" == "flask" ]]; then
|
elif [[ "${1}" == "flask" ]]; then
|
||||||
flask ${@:2}
|
flask ${@:2}
|
||||||
elif [[ "${1}" == "--help" || "${1}" == "-h" ]]; then
|
elif [[ "${1}" == "--help" || "${1}" == "-h" ]]; then
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
version: "3.5"
|
|
||||||
|
|
||||||
# The docker-compose.yml file is not meant to be modified itself.
|
# The docker-compose.yml file is not meant to be modified itself.
|
||||||
# Instead use the following files for configurations:
|
# Instead use the following files for configurations:
|
||||||
# - .env: Environment variables for the docker-compose.yml file.
|
# - .env: Environment variables for the docker-compose.yml file.
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
version: "3.5"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nopaque:
|
nopaque:
|
||||||
environment:
|
environment:
|
||||||
@ -13,6 +11,6 @@ services:
|
|||||||
- "./config.py:/home/nopaque/config.py"
|
- "./config.py:/home/nopaque/config.py"
|
||||||
- "./docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh"
|
- "./docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh"
|
||||||
- "./migrations:/home/nopaque/migrations"
|
- "./migrations:/home/nopaque/migrations"
|
||||||
- "./nopaque.py:/home/nopaque/nopaque.py"
|
|
||||||
- "./requirements.txt:/home/nopaque/requirements.txt"
|
- "./requirements.txt:/home/nopaque/requirements.txt"
|
||||||
- "./tests:/home/nopaque/tests"
|
- "./tests:/home/nopaque/tests"
|
||||||
|
- "./wsgi.py:/home/nopaque/wsgi.py"
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
version: "3.5"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nopaque:
|
nopaque:
|
||||||
environment:
|
environment:
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
version: "3.5"
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
traefik:
|
traefik:
|
||||||
external: true
|
external: true
|
||||||
|
@ -15,7 +15,7 @@ Flask-Marshmallow==0.14.0
|
|||||||
Flask-Menu==0.7.2
|
Flask-Menu==0.7.2
|
||||||
Flask-Migrate
|
Flask-Migrate
|
||||||
Flask-Paranoid
|
Flask-Paranoid
|
||||||
Flask-SocketIO
|
Flask-SocketIO==5.3.6
|
||||||
Flask-SQLAlchemy==2.5.1
|
Flask-SQLAlchemy==2.5.1
|
||||||
Flask-WTF
|
Flask-WTF
|
||||||
hiredis
|
hiredis
|
||||||
|
Reference in New Issue
Block a user