mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-11-15 01:05:42 +00:00
Restructure Contributions with nested blueprints
This commit is contained in:
parent
6bb4594937
commit
53bba2afb0
@ -3,3 +3,15 @@ from flask import Blueprint
|
|||||||
|
|
||||||
bp = Blueprint('contributions', __name__)
|
bp = Blueprint('contributions', __name__)
|
||||||
from . import routes
|
from . import routes
|
||||||
|
|
||||||
|
from .spacy_nlp_pipeline_models import bp as spacy_nlp_pipeline_models_bp
|
||||||
|
bp.register_blueprint(
|
||||||
|
spacy_nlp_pipeline_models_bp,
|
||||||
|
url_prefix='/spacy-nlp-pipeline-models'
|
||||||
|
)
|
||||||
|
|
||||||
|
from .tesseract_ocr_pipeline_models import bp as tesseract_ocr_pipeline_models_bp
|
||||||
|
bp.register_blueprint(
|
||||||
|
tesseract_ocr_pipeline_models_bp,
|
||||||
|
url_prefix='/tesseract-ocr-pipeline-models'
|
||||||
|
)
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
from flask import current_app
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from flask_wtf.file import FileField, FileRequired
|
|
||||||
from wtforms import (
|
from wtforms import (
|
||||||
BooleanField,
|
|
||||||
StringField,
|
StringField,
|
||||||
SubmitField,
|
SubmitField,
|
||||||
SelectMultipleField,
|
SelectMultipleField,
|
||||||
IntegerField,
|
IntegerField
|
||||||
ValidationError
|
|
||||||
)
|
)
|
||||||
from wtforms.validators import InputRequired, Length
|
from wtforms.validators import InputRequired, Length
|
||||||
from app.services import SERVICES
|
|
||||||
|
|
||||||
|
|
||||||
class ContributionBaseForm(FlaskForm):
|
class ContributionBaseForm(FlaskForm):
|
||||||
@ -48,74 +43,5 @@ class ContributionBaseForm(FlaskForm):
|
|||||||
submit = SubmitField()
|
submit = SubmitField()
|
||||||
|
|
||||||
|
|
||||||
class CreateTesseractOCRPipelineModelForm(ContributionBaseForm):
|
|
||||||
tesseract_model_file = FileField(
|
|
||||||
'File',
|
|
||||||
validators=[FileRequired()]
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate_tesseract_model_file(self, field):
|
|
||||||
if not field.data.filename.lower().endswith('.traineddata'):
|
|
||||||
raise ValidationError('traineddata files only!')
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
service_manifest = SERVICES['tesseract-ocr-pipeline']
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.compatible_service_versions.choices = [('', 'Choose your option')]
|
|
||||||
self.compatible_service_versions.choices += [
|
|
||||||
(x, x) for x in service_manifest['versions'].keys()
|
|
||||||
]
|
|
||||||
self.compatible_service_versions.default = ''
|
|
||||||
|
|
||||||
|
|
||||||
class CreateSpaCyNLPPipelineModelForm(ContributionBaseForm):
|
|
||||||
spacy_model_file = FileField(
|
|
||||||
'File',
|
|
||||||
validators=[FileRequired()]
|
|
||||||
)
|
|
||||||
pipeline_name = StringField(
|
|
||||||
'Pipeline name',
|
|
||||||
validators=[InputRequired(), Length(max=64)]
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate_spacy_model_file(self, field):
|
|
||||||
if not field.data.filename.lower().endswith('.tar.gz'):
|
|
||||||
raise ValidationError('.tar.gz files only!')
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
service_manifest = SERVICES['spacy-nlp-pipeline']
|
|
||||||
self.compatible_service_versions.choices = [('', 'Choose your option')]
|
|
||||||
self.compatible_service_versions.choices += [
|
|
||||||
(x, x) for x in service_manifest['versions'].keys()
|
|
||||||
]
|
|
||||||
self.compatible_service_versions.default = ''
|
|
||||||
|
|
||||||
|
|
||||||
class EditContributionBaseForm(ContributionBaseForm):
|
class EditContributionBaseForm(ContributionBaseForm):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class EditTesseractOCRPipelineModelForm(EditContributionBaseForm):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
service_manifest = SERVICES['tesseract-ocr-pipeline']
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.compatible_service_versions.choices = [('', 'Choose your option')]
|
|
||||||
self.compatible_service_versions.choices += [
|
|
||||||
(x, x) for x in service_manifest['versions'].keys()
|
|
||||||
]
|
|
||||||
self.compatible_service_versions.default = ''
|
|
||||||
|
|
||||||
|
|
||||||
class EditSpaCyNLPPipelineModelForm(EditContributionBaseForm):
|
|
||||||
pipeline_name = StringField(
|
|
||||||
'Pipeline name',
|
|
||||||
validators=[InputRequired(), Length(max=64)]
|
|
||||||
)
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
service_manifest = SERVICES['spacy-nlp-pipeline']
|
|
||||||
self.compatible_service_versions.choices = [('', 'Choose your option')]
|
|
||||||
self.compatible_service_versions.choices += [
|
|
||||||
(x, x) for x in service_manifest['versions'].keys()
|
|
||||||
]
|
|
||||||
self.compatible_service_versions.default = ''
|
|
||||||
|
@ -1,369 +1,12 @@
|
|||||||
from flask import (
|
from flask import render_template
|
||||||
abort,
|
from flask_login import login_required
|
||||||
current_app,
|
|
||||||
flash,
|
|
||||||
jsonify,
|
|
||||||
Markup,
|
|
||||||
redirect,
|
|
||||||
render_template,
|
|
||||||
request,
|
|
||||||
url_for
|
|
||||||
)
|
|
||||||
from flask_login import login_required, current_user
|
|
||||||
from threading import Thread
|
|
||||||
from app import db
|
|
||||||
from app.decorators import content_negotiation, permission_required
|
|
||||||
from app.models import SpaCyNLPPipelineModel, TesseractOCRPipelineModel
|
|
||||||
from . import bp
|
from . import bp
|
||||||
from .forms import (
|
|
||||||
CreateSpaCyNLPPipelineModelForm,
|
|
||||||
CreateTesseractOCRPipelineModelForm,
|
|
||||||
EditSpaCyNLPPipelineModelForm,
|
|
||||||
EditTesseractOCRPipelineModelForm
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.before_request
|
|
||||||
@login_required
|
|
||||||
def before_request():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/')
|
@bp.route('/')
|
||||||
|
@login_required
|
||||||
def contributions():
|
def contributions():
|
||||||
return render_template(
|
return render_template(
|
||||||
'contributions/contributions.html.j2',
|
'contributions/contributions.html.j2',
|
||||||
title='Contributions'
|
title='Contributions'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# SpaCy NLP Pipeline Models #
|
|
||||||
##############################################################################
|
|
||||||
#region spacy-nlp-pipeline-models
|
|
||||||
@bp.route('/spacy-nlp-pipeline-models')
|
|
||||||
def spacy_nlp_pipeline_models():
|
|
||||||
return render_template(
|
|
||||||
'contributions/spacy_nlp_pipeline_models.html.j2',
|
|
||||||
title='SpaCy NLP Pipeline Models'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/spacy-nlp-pipeline-models/create', methods=['GET', 'POST'])
|
|
||||||
def create_spacy_nlp_pipeline_model():
|
|
||||||
form = CreateSpaCyNLPPipelineModelForm(prefix='create-spacy-nlp-pipeline-model-form')
|
|
||||||
if form.is_submitted():
|
|
||||||
if not form.validate():
|
|
||||||
response = {'errors': form.errors}
|
|
||||||
return response, 400
|
|
||||||
try:
|
|
||||||
spacy_nlp_pipeline_model = SpaCyNLPPipelineModel.create(
|
|
||||||
form.spacy_model_file.data,
|
|
||||||
compatible_service_versions=form.compatible_service_versions.data,
|
|
||||||
description=form.description.data,
|
|
||||||
pipeline_name=form.pipeline_name.data,
|
|
||||||
publisher=form.publisher.data,
|
|
||||||
publisher_url=form.publisher_url.data,
|
|
||||||
publishing_url=form.publishing_url.data,
|
|
||||||
publishing_year=form.publishing_year.data,
|
|
||||||
is_public=False,
|
|
||||||
title=form.title.data,
|
|
||||||
version=form.version.data,
|
|
||||||
user=current_user
|
|
||||||
)
|
|
||||||
except OSError:
|
|
||||||
abort(500)
|
|
||||||
db.session.commit()
|
|
||||||
spacy_nlp_pipeline_model_url = url_for(
|
|
||||||
'.spacy_nlp_pipeline_model',
|
|
||||||
spacy_nlp_pipeline_model_id=spacy_nlp_pipeline_model.id
|
|
||||||
)
|
|
||||||
message = Markup(f'SpaCy NLP Pipeline model "<a href="{spacy_nlp_pipeline_model_url}">{spacy_nlp_pipeline_model.title}</a>" created')
|
|
||||||
flash(message)
|
|
||||||
return {}, 201, {'Location': spacy_nlp_pipeline_model_url}
|
|
||||||
return render_template(
|
|
||||||
'contributions/create_spacy_nlp_pipeline_model.html.j2',
|
|
||||||
form=form,
|
|
||||||
title='Create SpaCy NLP Pipeline Model'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/spacy-nlp-pipeline-models/<hashid:spacy_nlp_pipeline_model_id>', methods=['GET', 'POST'])
|
|
||||||
def spacy_nlp_pipeline_model(spacy_nlp_pipeline_model_id):
|
|
||||||
spacy_nlp_pipeline_model = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id)
|
|
||||||
form = EditSpaCyNLPPipelineModelForm(
|
|
||||||
data=spacy_nlp_pipeline_model.to_json_serializeable(),
|
|
||||||
prefix='edit-spacy-nlp-pipeline-model-form'
|
|
||||||
)
|
|
||||||
if form.validate_on_submit():
|
|
||||||
form.populate_obj(spacy_nlp_pipeline_model)
|
|
||||||
if db.session.is_modified(spacy_nlp_pipeline_model):
|
|
||||||
message = Markup(f'SpaCy NLP Pipeline model "<a href="{spacy_nlp_pipeline_model.url}">{spacy_nlp_pipeline_model.title}</a>" updated')
|
|
||||||
flash(message)
|
|
||||||
db.session.commit()
|
|
||||||
return redirect(url_for('.spacy_nlp_pipeline_models'))
|
|
||||||
return render_template(
|
|
||||||
'contributions/spacy_nlp_pipeline_model.html.j2',
|
|
||||||
form=form,
|
|
||||||
spacy_nlp_pipeline_model=spacy_nlp_pipeline_model,
|
|
||||||
title=f'{spacy_nlp_pipeline_model.title} {spacy_nlp_pipeline_model.version}'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#region json-routes
|
|
||||||
@bp.route('/spacy-nlp-pipeline-models/<hashid:spacy_nlp_pipeline_model_id>', methods=['DELETE'])
|
|
||||||
@login_required
|
|
||||||
@content_negotiation(produces='application/json')
|
|
||||||
def delete_spacy_model(spacy_nlp_pipeline_model_id):
|
|
||||||
def _delete_spacy_model(app, spacy_nlp_pipeline_model_id):
|
|
||||||
with app.app_context():
|
|
||||||
snpm = SpaCyNLPPipelineModel.query.get(spacy_nlp_pipeline_model_id)
|
|
||||||
snpm.delete()
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
snpm = SpaCyNLPPipelineModel.query.get(spacy_nlp_pipeline_model_id)
|
|
||||||
if snpm is None:
|
|
||||||
resonse_data = {
|
|
||||||
'message': (
|
|
||||||
'SpaCy NLP Pipeline Model with id'
|
|
||||||
f' "{spacy_nlp_pipeline_model_id}" not found'
|
|
||||||
),
|
|
||||||
'category': 'error'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 404
|
|
||||||
return response
|
|
||||||
if not (snpm.user == current_user or current_user.is_administrator()):
|
|
||||||
resonse_data = {
|
|
||||||
'message': f'You are not allowed to delete "{snpm.title}"',
|
|
||||||
'category': 'error'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 403
|
|
||||||
return response
|
|
||||||
thread = Thread(
|
|
||||||
target=_delete_spacy_model,
|
|
||||||
args=(current_app._get_current_object(), snpm.id)
|
|
||||||
)
|
|
||||||
thread.start()
|
|
||||||
resonse_data = {
|
|
||||||
'message': \
|
|
||||||
f'SpaCy NLP Pipeline Model "{snpm.title}" marked for deletion'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 202
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/spacy-nlp-pipeline-models/<hashid:spacy_nlp_pipeline_model_id>/is_public', methods=['PUT'])
|
|
||||||
@login_required
|
|
||||||
@permission_required('CONTRIBUTE')
|
|
||||||
@content_negotiation(consumes='application/json', produces='application/json')
|
|
||||||
def update_spacy_nlp_pipeline_model_is_public(spacy_nlp_pipeline_model_id):
|
|
||||||
is_public = request.json
|
|
||||||
if not isinstance(is_public, bool):
|
|
||||||
resonse_data = {
|
|
||||||
'message': 'Request body must be a boolean',
|
|
||||||
'category': 'error'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 400
|
|
||||||
return response
|
|
||||||
snpm = SpaCyNLPPipelineModel.query.get(spacy_nlp_pipeline_model_id)
|
|
||||||
if snpm is None:
|
|
||||||
resonse_data = {
|
|
||||||
'message': (
|
|
||||||
'SpaCy NLP Pipeline Model with id'
|
|
||||||
f' "{spacy_nlp_pipeline_model_id}" not found'
|
|
||||||
),
|
|
||||||
'category': 'error'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 404
|
|
||||||
return response
|
|
||||||
if not (snpm.user == current_user or current_user.is_administrator()):
|
|
||||||
resonse_data = {
|
|
||||||
'message': f'You are not allowed to delete "{snpm.title}"',
|
|
||||||
'category': 'error'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 403
|
|
||||||
return response
|
|
||||||
snpm.is_public = is_public
|
|
||||||
db.session.commit()
|
|
||||||
response_data = {
|
|
||||||
'message': (
|
|
||||||
f'SpaCy NLP Pipeline Model "{snpm.title}"'
|
|
||||||
f' is now {"public" if is_public else "private"}'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
response = jsonify(response_data)
|
|
||||||
response.status_code = 200
|
|
||||||
return response
|
|
||||||
#endregion json-routes
|
|
||||||
#endregion spacy-nlp-pipeline-models
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# Tesseract OCR Pipeline Models #
|
|
||||||
##############################################################################
|
|
||||||
#region tesseract-ocr-pipeline-models
|
|
||||||
@bp.route('/tesseract-ocr-pipeline-models')
|
|
||||||
def tesseract_ocr_pipeline_models():
|
|
||||||
return render_template(
|
|
||||||
'contributions/tesseract_ocr_pipeline_models.html.j2',
|
|
||||||
title='Tesseract OCR Pipeline Models'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/tesseract-ocr-pipeline-models/create', methods=['GET', 'POST'])
|
|
||||||
def create_tesseract_ocr_pipeline_model():
|
|
||||||
form = CreateTesseractOCRPipelineModelForm(prefix='create-tesseract-ocr-pipeline-model-form')
|
|
||||||
if form.is_submitted():
|
|
||||||
if not form.validate():
|
|
||||||
response = {'errors': form.errors}
|
|
||||||
return response, 400
|
|
||||||
try:
|
|
||||||
tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.create(
|
|
||||||
form.tesseract_model_file.data,
|
|
||||||
compatible_service_versions=form.compatible_service_versions.data,
|
|
||||||
description=form.description.data,
|
|
||||||
publisher=form.publisher.data,
|
|
||||||
publisher_url=form.publisher_url.data,
|
|
||||||
publishing_url=form.publishing_url.data,
|
|
||||||
publishing_year=form.publishing_year.data,
|
|
||||||
is_public=False,
|
|
||||||
title=form.title.data,
|
|
||||||
version=form.version.data,
|
|
||||||
user=current_user
|
|
||||||
)
|
|
||||||
except OSError:
|
|
||||||
abort(500)
|
|
||||||
db.session.commit()
|
|
||||||
tesseract_ocr_pipeline_model_url = url_for(
|
|
||||||
'.tesseract_ocr_pipeline_model',
|
|
||||||
tesseract_ocr_pipeline_model_id=tesseract_ocr_pipeline_model.id
|
|
||||||
)
|
|
||||||
message = Markup(f'Tesseract OCR Pipeline model "<a href="{tesseract_ocr_pipeline_model_url}">{tesseract_ocr_pipeline_model.title}</a>" created')
|
|
||||||
flash(message)
|
|
||||||
return {}, 201, {'Location': tesseract_ocr_pipeline_model_url}
|
|
||||||
return render_template(
|
|
||||||
'contributions/create_tesseract_ocr_pipeline_model.html.j2',
|
|
||||||
form=form,
|
|
||||||
title='Create Tesseract OCR Pipeline Model'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/tesseract-ocr-pipeline-models/<hashid:tesseract_ocr_pipeline_model_id>', methods=['GET', 'POST'])
|
|
||||||
def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id):
|
|
||||||
tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id)
|
|
||||||
form = EditTesseractOCRPipelineModelForm(
|
|
||||||
data=tesseract_ocr_pipeline_model.to_json_serializeable(),
|
|
||||||
prefix='edit-tesseract-ocr-pipeline-model-form'
|
|
||||||
)
|
|
||||||
if form.validate_on_submit():
|
|
||||||
form.populate_obj(tesseract_ocr_pipeline_model)
|
|
||||||
if db.session.is_modified(tesseract_ocr_pipeline_model):
|
|
||||||
message = Markup(f'Tesseract OCR Pipeline model "<a href="{tesseract_ocr_pipeline_model.url}">{tesseract_ocr_pipeline_model.title}</a>" updated')
|
|
||||||
flash(message)
|
|
||||||
db.session.commit()
|
|
||||||
return redirect(url_for('.tesseract_ocr_pipeline_models'))
|
|
||||||
return render_template(
|
|
||||||
'contributions/tesseract_ocr_pipeline_model.html.j2',
|
|
||||||
form=form,
|
|
||||||
tesseract_ocr_pipeline_model=tesseract_ocr_pipeline_model,
|
|
||||||
title=f'{tesseract_ocr_pipeline_model.title} {tesseract_ocr_pipeline_model.version}'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#region json-routes
|
|
||||||
@bp.route('/tesseract-ocr-pipeline-models/<hashid:tesseract_ocr_pipeline_model_id>', methods=['DELETE'])
|
|
||||||
@login_required
|
|
||||||
@content_negotiation(produces='application/json')
|
|
||||||
def delete_tesseract_model(tesseract_ocr_pipeline_model_id):
|
|
||||||
def _delete_tesseract_ocr_pipeline_model(app, tesseract_ocr_pipeline_model_id):
|
|
||||||
with app.app_context():
|
|
||||||
topm = TesseractOCRPipelineModel.query.get(tesseract_ocr_pipeline_model_id)
|
|
||||||
topm.delete()
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
topm = TesseractOCRPipelineModel.query.get(tesseract_ocr_pipeline_model_id)
|
|
||||||
if topm is None:
|
|
||||||
resonse_data = {
|
|
||||||
'message': (
|
|
||||||
'Tesseract OCR Pipeline Model with id'
|
|
||||||
f' "{tesseract_ocr_pipeline_model_id}" not found'
|
|
||||||
),
|
|
||||||
'category': 'error'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 404
|
|
||||||
return response
|
|
||||||
if not (topm.user == current_user or current_user.is_administrator()):
|
|
||||||
resonse_data = {
|
|
||||||
'message': f'You are not allowed to delete "{topm.title}"',
|
|
||||||
'category': 'error'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 403
|
|
||||||
return response
|
|
||||||
thread = Thread(
|
|
||||||
target=_delete_tesseract_ocr_pipeline_model,
|
|
||||||
args=(current_app._get_current_object(), topm.id)
|
|
||||||
)
|
|
||||||
thread.start()
|
|
||||||
resonse_data = {
|
|
||||||
'message': \
|
|
||||||
f'Tesseract OCR Pipeline Model "{topm.title}" marked for deletion'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 202
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/tesseract-ocr-pipeline-models/<hashid:tesseract_ocr_pipeline_model_id>/is_public', methods=['PUT'])
|
|
||||||
@login_required
|
|
||||||
@permission_required('CONTRIBUTE')
|
|
||||||
@content_negotiation(consumes='application/json', produces='application/json')
|
|
||||||
def update_tesseract_ocr_pipeline_model_is_public(tesseract_ocr_pipeline_model_id):
|
|
||||||
is_public = request.json
|
|
||||||
if not isinstance(is_public, bool):
|
|
||||||
resonse_data = {
|
|
||||||
'message': 'Request body must be a boolean',
|
|
||||||
'category': 'error'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 400
|
|
||||||
return response
|
|
||||||
topm = TesseractOCRPipelineModel.query.get(tesseract_ocr_pipeline_model_id)
|
|
||||||
if topm is None:
|
|
||||||
resonse_data = {
|
|
||||||
'message': (
|
|
||||||
'Tesseract OCR Pipeline Model with id'
|
|
||||||
f' "{tesseract_ocr_pipeline_model_id}" not found'
|
|
||||||
),
|
|
||||||
'category': 'error'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 404
|
|
||||||
return response
|
|
||||||
if not (topm.user == current_user or current_user.is_administrator()):
|
|
||||||
resonse_data = {
|
|
||||||
'message': f'You are not allowed to delete "{topm.title}"',
|
|
||||||
'category': 'error'
|
|
||||||
}
|
|
||||||
response = jsonify(resonse_data)
|
|
||||||
response.status_code = 403
|
|
||||||
return response
|
|
||||||
topm.is_public = is_public
|
|
||||||
db.session.commit()
|
|
||||||
response_data = {
|
|
||||||
'message': (
|
|
||||||
f'Tesseract OCR Pipeline Model "{topm.title}"'
|
|
||||||
f' is now {"public" if is_public else "private"}'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
response = jsonify(response_data)
|
|
||||||
response.status_code = 200
|
|
||||||
return response
|
|
||||||
#endregion json-routes
|
|
||||||
#endregion tesseract-ocr-pipeline-models
|
|
||||||
|
8
app/contributions/spacy_nlp_pipeline_models/__init__.py
Normal file
8
app/contributions/spacy_nlp_pipeline_models/__init__.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
|
||||||
|
TEMPLATE_FOLDER = 'contributions/spacy_nlp_pipeline_models'
|
||||||
|
|
||||||
|
|
||||||
|
bp = Blueprint('spacy_nlp_pipeline_models', __name__)
|
||||||
|
from . import routes, json_routes
|
44
app/contributions/spacy_nlp_pipeline_models/forms.py
Normal file
44
app/contributions/spacy_nlp_pipeline_models/forms.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
from flask_wtf.file import FileField, FileRequired
|
||||||
|
from wtforms import StringField, ValidationError
|
||||||
|
from wtforms.validators import InputRequired, Length
|
||||||
|
from app.services import SERVICES
|
||||||
|
from ..forms import ContributionBaseForm, EditContributionBaseForm
|
||||||
|
|
||||||
|
|
||||||
|
class CreateSpaCyNLPPipelineModelForm(ContributionBaseForm):
|
||||||
|
spacy_model_file = FileField(
|
||||||
|
'File',
|
||||||
|
validators=[FileRequired()]
|
||||||
|
)
|
||||||
|
pipeline_name = StringField(
|
||||||
|
'Pipeline name',
|
||||||
|
validators=[InputRequired(), Length(max=64)]
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate_spacy_model_file(self, field):
|
||||||
|
if not field.data.filename.lower().endswith('.tar.gz'):
|
||||||
|
raise ValidationError('.tar.gz files only!')
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
service_manifest = SERVICES['spacy-nlp-pipeline']
|
||||||
|
self.compatible_service_versions.choices = [('', 'Choose your option')]
|
||||||
|
self.compatible_service_versions.choices += [
|
||||||
|
(x, x) for x in service_manifest['versions'].keys()
|
||||||
|
]
|
||||||
|
self.compatible_service_versions.default = ''
|
||||||
|
|
||||||
|
|
||||||
|
class EditSpaCyNLPPipelineModelForm(EditContributionBaseForm):
|
||||||
|
pipeline_name = StringField(
|
||||||
|
'Pipeline name',
|
||||||
|
validators=[InputRequired(), Length(max=64)]
|
||||||
|
)
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
service_manifest = SERVICES['spacy-nlp-pipeline']
|
||||||
|
self.compatible_service_versions.choices = [('', 'Choose your option')]
|
||||||
|
self.compatible_service_versions.choices += [
|
||||||
|
(x, x) for x in service_manifest['versions'].keys()
|
||||||
|
]
|
||||||
|
self.compatible_service_versions.default = ''
|
58
app/contributions/spacy_nlp_pipeline_models/json_routes.py
Normal file
58
app/contributions/spacy_nlp_pipeline_models/json_routes.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
from flask import abort, current_app, jsonify, request
|
||||||
|
from flask_login import login_required, current_user
|
||||||
|
from threading import Thread
|
||||||
|
from app import db
|
||||||
|
from app.decorators import content_negotiation, permission_required
|
||||||
|
from app.models import SpaCyNLPPipelineModel
|
||||||
|
from . import bp
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/<hashid:spacy_nlp_pipeline_model_id>', methods=['DELETE'])
|
||||||
|
@login_required
|
||||||
|
@content_negotiation(produces='application/json')
|
||||||
|
def delete_spacy_model(spacy_nlp_pipeline_model_id):
|
||||||
|
def _delete_spacy_model(app, spacy_nlp_pipeline_model_id):
|
||||||
|
with app.app_context():
|
||||||
|
snpm = SpaCyNLPPipelineModel.query.get(spacy_nlp_pipeline_model_id)
|
||||||
|
snpm.delete()
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
snpm = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id)
|
||||||
|
if not (snpm.user == current_user or current_user.is_administrator()):
|
||||||
|
abort(403)
|
||||||
|
thread = Thread(
|
||||||
|
target=_delete_spacy_model,
|
||||||
|
args=(current_app._get_current_object(), snpm.id)
|
||||||
|
)
|
||||||
|
thread.start()
|
||||||
|
resonse_data = {
|
||||||
|
'message': \
|
||||||
|
f'SpaCy NLP Pipeline Model "{snpm.title}" marked for deletion'
|
||||||
|
}
|
||||||
|
response = jsonify(resonse_data)
|
||||||
|
response.status_code = 202
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/<hashid:spacy_nlp_pipeline_model_id>/is_public', methods=['PUT'])
|
||||||
|
@login_required
|
||||||
|
@permission_required('CONTRIBUTE')
|
||||||
|
@content_negotiation(consumes='application/json', produces='application/json')
|
||||||
|
def update_spacy_nlp_pipeline_model_is_public(spacy_nlp_pipeline_model_id):
|
||||||
|
is_public = request.json
|
||||||
|
if not isinstance(is_public, bool):
|
||||||
|
abort(400)
|
||||||
|
snpm = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id)
|
||||||
|
if not (snpm.user == current_user or current_user.is_administrator()):
|
||||||
|
abort(403)
|
||||||
|
snpm.is_public = is_public
|
||||||
|
db.session.commit()
|
||||||
|
response_data = {
|
||||||
|
'message': (
|
||||||
|
f'SpaCy NLP Pipeline Model "{snpm.title}"'
|
||||||
|
f' is now {"public" if is_public else "private"}'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
response = jsonify(response_data)
|
||||||
|
response.status_code = 200
|
||||||
|
return response
|
87
app/contributions/spacy_nlp_pipeline_models/routes.py
Normal file
87
app/contributions/spacy_nlp_pipeline_models/routes.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
from flask import abort, flash, Markup, redirect, render_template, url_for
|
||||||
|
from flask_login import login_required, current_user
|
||||||
|
from app import db
|
||||||
|
from app.models import SpaCyNLPPipelineModel
|
||||||
|
from . import bp, TEMPLATE_FOLDER
|
||||||
|
from .forms import (
|
||||||
|
CreateSpaCyNLPPipelineModelForm,
|
||||||
|
EditSpaCyNLPPipelineModelForm
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('')
|
||||||
|
@login_required
|
||||||
|
def spacy_nlp_pipeline_models():
|
||||||
|
return render_template(
|
||||||
|
f'{TEMPLATE_FOLDER}/spacy_nlp_pipeline_models.html.j2',
|
||||||
|
title='SpaCy NLP Pipeline Models'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/create', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def create_spacy_nlp_pipeline_model():
|
||||||
|
form = CreateSpaCyNLPPipelineModelForm(prefix='create-spacy-nlp-pipeline-model-form')
|
||||||
|
if form.is_submitted():
|
||||||
|
if not form.validate():
|
||||||
|
response = {'errors': form.errors}
|
||||||
|
return response, 400
|
||||||
|
try:
|
||||||
|
spacy_nlp_pipeline_model = SpaCyNLPPipelineModel.create(
|
||||||
|
form.spacy_model_file.data,
|
||||||
|
compatible_service_versions=form.compatible_service_versions.data,
|
||||||
|
description=form.description.data,
|
||||||
|
pipeline_name=form.pipeline_name.data,
|
||||||
|
publisher=form.publisher.data,
|
||||||
|
publisher_url=form.publisher_url.data,
|
||||||
|
publishing_url=form.publishing_url.data,
|
||||||
|
publishing_year=form.publishing_year.data,
|
||||||
|
is_public=False,
|
||||||
|
title=form.title.data,
|
||||||
|
version=form.version.data,
|
||||||
|
user=current_user
|
||||||
|
)
|
||||||
|
except OSError:
|
||||||
|
abort(500)
|
||||||
|
db.session.commit()
|
||||||
|
spacy_nlp_pipeline_model_url = url_for(
|
||||||
|
'.spacy_nlp_pipeline_model',
|
||||||
|
spacy_nlp_pipeline_model_id=spacy_nlp_pipeline_model.id
|
||||||
|
)
|
||||||
|
message = Markup(
|
||||||
|
f'SpaCy NLP Pipeline model "{spacy_nlp_pipeline_model.title}" '
|
||||||
|
'created'
|
||||||
|
)
|
||||||
|
flash(message)
|
||||||
|
return '', 201, {'Location': spacy_nlp_pipeline_model_url}
|
||||||
|
return render_template(
|
||||||
|
f'{TEMPLATE_FOLDER}/create_spacy_nlp_pipeline_model.html.j2',
|
||||||
|
form=form,
|
||||||
|
title='Create SpaCy NLP Pipeline Model'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/<hashid:spacy_nlp_pipeline_model_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def spacy_nlp_pipeline_model(spacy_nlp_pipeline_model_id):
|
||||||
|
spacy_nlp_pipeline_model = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id)
|
||||||
|
form = EditSpaCyNLPPipelineModelForm(
|
||||||
|
data=spacy_nlp_pipeline_model.to_json_serializeable(),
|
||||||
|
prefix='edit-spacy-nlp-pipeline-model-form'
|
||||||
|
)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
form.populate_obj(spacy_nlp_pipeline_model)
|
||||||
|
if db.session.is_modified(spacy_nlp_pipeline_model):
|
||||||
|
message = Markup(
|
||||||
|
f'SpaCy NLP Pipeline model "{spacy_nlp_pipeline_model.title}" '
|
||||||
|
'updated'
|
||||||
|
)
|
||||||
|
flash(message)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('.spacy_nlp_pipeline_models'))
|
||||||
|
return render_template(
|
||||||
|
f'{TEMPLATE_FOLDER}/spacy_nlp_pipeline_model.html.j2',
|
||||||
|
form=form,
|
||||||
|
spacy_nlp_pipeline_model=spacy_nlp_pipeline_model,
|
||||||
|
title=f'{spacy_nlp_pipeline_model.title} {spacy_nlp_pipeline_model.version}'
|
||||||
|
)
|
@ -0,0 +1,8 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
|
||||||
|
TEMPLATE_FOLDER = 'contributions/tesseract_ocr_pipeline_models'
|
||||||
|
|
||||||
|
|
||||||
|
bp = Blueprint('tesseract_ocr_pipeline_models', __name__)
|
||||||
|
from . import routes, json_routes
|
35
app/contributions/tesseract_ocr_pipeline_models/forms.py
Normal file
35
app/contributions/tesseract_ocr_pipeline_models/forms.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from flask_wtf.file import FileField, FileRequired
|
||||||
|
from wtforms import ValidationError
|
||||||
|
from app.services import SERVICES
|
||||||
|
from ..forms import ContributionBaseForm, EditContributionBaseForm
|
||||||
|
|
||||||
|
|
||||||
|
class CreateTesseractOCRPipelineModelForm(ContributionBaseForm):
|
||||||
|
tesseract_model_file = FileField(
|
||||||
|
'File',
|
||||||
|
validators=[FileRequired()]
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate_tesseract_model_file(self, field):
|
||||||
|
if not field.data.filename.lower().endswith('.traineddata'):
|
||||||
|
raise ValidationError('traineddata files only!')
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
service_manifest = SERVICES['tesseract-ocr-pipeline']
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.compatible_service_versions.choices = [('', 'Choose your option')]
|
||||||
|
self.compatible_service_versions.choices += [
|
||||||
|
(x, x) for x in service_manifest['versions'].keys()
|
||||||
|
]
|
||||||
|
self.compatible_service_versions.default = ''
|
||||||
|
|
||||||
|
|
||||||
|
class EditTesseractOCRPipelineModelForm(EditContributionBaseForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
service_manifest = SERVICES['tesseract-ocr-pipeline']
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.compatible_service_versions.choices = [('', 'Choose your option')]
|
||||||
|
self.compatible_service_versions.choices += [
|
||||||
|
(x, x) for x in service_manifest['versions'].keys()
|
||||||
|
]
|
||||||
|
self.compatible_service_versions.default = ''
|
@ -0,0 +1,58 @@
|
|||||||
|
from flask import abort, current_app, jsonify, request
|
||||||
|
from flask_login import login_required, current_user
|
||||||
|
from threading import Thread
|
||||||
|
from app import db
|
||||||
|
from app.decorators import content_negotiation, permission_required
|
||||||
|
from app.models import TesseractOCRPipelineModel
|
||||||
|
from . import bp
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/<hashid:tesseract_ocr_pipeline_model_id>', methods=['DELETE'])
|
||||||
|
@login_required
|
||||||
|
@content_negotiation(produces='application/json')
|
||||||
|
def delete_tesseract_model(tesseract_ocr_pipeline_model_id):
|
||||||
|
def _delete_tesseract_ocr_pipeline_model(app, tesseract_ocr_pipeline_model_id):
|
||||||
|
with app.app_context():
|
||||||
|
topm = TesseractOCRPipelineModel.query.get(tesseract_ocr_pipeline_model_id)
|
||||||
|
topm.delete()
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
topm = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id)
|
||||||
|
if not (topm.user == current_user or current_user.is_administrator()):
|
||||||
|
abort(403)
|
||||||
|
thread = Thread(
|
||||||
|
target=_delete_tesseract_ocr_pipeline_model,
|
||||||
|
args=(current_app._get_current_object(), topm.id)
|
||||||
|
)
|
||||||
|
thread.start()
|
||||||
|
resonse_data = {
|
||||||
|
'message': \
|
||||||
|
f'Tesseract OCR Pipeline Model "{topm.title}" marked for deletion'
|
||||||
|
}
|
||||||
|
response = jsonify(resonse_data)
|
||||||
|
response.status_code = 202
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/<hashid:tesseract_ocr_pipeline_model_id>/is_public', methods=['PUT'])
|
||||||
|
@login_required
|
||||||
|
@permission_required('CONTRIBUTE')
|
||||||
|
@content_negotiation(consumes='application/json', produces='application/json')
|
||||||
|
def update_tesseract_ocr_pipeline_model_is_public(tesseract_ocr_pipeline_model_id):
|
||||||
|
is_public = request.json
|
||||||
|
if not isinstance(is_public, bool):
|
||||||
|
abort(400)
|
||||||
|
topm = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id)
|
||||||
|
if not (topm.user == current_user or current_user.is_administrator()):
|
||||||
|
abort(403)
|
||||||
|
topm.is_public = is_public
|
||||||
|
db.session.commit()
|
||||||
|
response_data = {
|
||||||
|
'message': (
|
||||||
|
f'Tesseract OCR Pipeline Model "{topm.title}"'
|
||||||
|
f' is now {"public" if is_public else "private"}'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
response = jsonify(response_data)
|
||||||
|
response.status_code = 200
|
||||||
|
return response
|
80
app/contributions/tesseract_ocr_pipeline_models/routes.py
Normal file
80
app/contributions/tesseract_ocr_pipeline_models/routes.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
from flask import abort, flash, Markup, redirect, render_template, url_for
|
||||||
|
from flask_login import login_required, current_user
|
||||||
|
from app import db
|
||||||
|
from app.models import TesseractOCRPipelineModel
|
||||||
|
from . import bp, TEMPLATE_FOLDER
|
||||||
|
from .forms import (
|
||||||
|
CreateTesseractOCRPipelineModelForm,
|
||||||
|
EditTesseractOCRPipelineModelForm
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('')
|
||||||
|
@login_required
|
||||||
|
def tesseract_ocr_pipeline_models():
|
||||||
|
return render_template(
|
||||||
|
f'{TEMPLATE_FOLDER}/tesseract_ocr_pipeline_models.html.j2',
|
||||||
|
title='Tesseract OCR Pipeline Models'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/create', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def create_tesseract_ocr_pipeline_model():
|
||||||
|
form = CreateTesseractOCRPipelineModelForm(prefix='create-tesseract-ocr-pipeline-model-form')
|
||||||
|
if form.is_submitted():
|
||||||
|
if not form.validate():
|
||||||
|
response = {'errors': form.errors}
|
||||||
|
return response, 400
|
||||||
|
try:
|
||||||
|
tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.create(
|
||||||
|
form.tesseract_model_file.data,
|
||||||
|
compatible_service_versions=form.compatible_service_versions.data,
|
||||||
|
description=form.description.data,
|
||||||
|
publisher=form.publisher.data,
|
||||||
|
publisher_url=form.publisher_url.data,
|
||||||
|
publishing_url=form.publishing_url.data,
|
||||||
|
publishing_year=form.publishing_year.data,
|
||||||
|
is_public=False,
|
||||||
|
title=form.title.data,
|
||||||
|
version=form.version.data,
|
||||||
|
user=current_user
|
||||||
|
)
|
||||||
|
except OSError:
|
||||||
|
abort(500)
|
||||||
|
db.session.commit()
|
||||||
|
tesseract_ocr_pipeline_model_url = url_for(
|
||||||
|
'.tesseract_ocr_pipeline_model',
|
||||||
|
tesseract_ocr_pipeline_model_id=tesseract_ocr_pipeline_model.id
|
||||||
|
)
|
||||||
|
message = Markup(f'Tesseract OCR Pipeline model "<a href="{tesseract_ocr_pipeline_model_url}">{tesseract_ocr_pipeline_model.title}</a>" created')
|
||||||
|
flash(message)
|
||||||
|
return {}, 201, {'Location': tesseract_ocr_pipeline_model_url}
|
||||||
|
return render_template(
|
||||||
|
f'{TEMPLATE_FOLDER}/create_tesseract_ocr_pipeline_model.html.j2',
|
||||||
|
form=form,
|
||||||
|
title='Create Tesseract OCR Pipeline Model'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/<hashid:tesseract_ocr_pipeline_model_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id):
|
||||||
|
tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id)
|
||||||
|
form = EditTesseractOCRPipelineModelForm(
|
||||||
|
data=tesseract_ocr_pipeline_model.to_json_serializeable(),
|
||||||
|
prefix='edit-tesseract-ocr-pipeline-model-form'
|
||||||
|
)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
form.populate_obj(tesseract_ocr_pipeline_model)
|
||||||
|
if db.session.is_modified(tesseract_ocr_pipeline_model):
|
||||||
|
message = Markup(f'Tesseract OCR Pipeline model "<a href="{tesseract_ocr_pipeline_model.url}">{tesseract_ocr_pipeline_model.title}</a>" updated')
|
||||||
|
flash(message)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('.tesseract_ocr_pipeline_models'))
|
||||||
|
return render_template(
|
||||||
|
f'{TEMPLATE_FOLDER}/tesseract_ocr_pipeline_model.html.j2',
|
||||||
|
form=form,
|
||||||
|
tesseract_ocr_pipeline_model=tesseract_ocr_pipeline_model,
|
||||||
|
title=f'{tesseract_ocr_pipeline_model.title} {tesseract_ocr_pipeline_model.version}'
|
||||||
|
)
|
@ -1,6 +1,5 @@
|
|||||||
{% extends "base.html.j2" %}
|
{% extends "base.html.j2" %}
|
||||||
{% import "materialize/wtf.html.j2" as wtf %}
|
{% import "materialize/wtf.html.j2" as wtf %}
|
||||||
{% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %}
|
|
||||||
|
|
||||||
{% block page_content %}
|
{% block page_content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -11,7 +10,7 @@
|
|||||||
|
|
||||||
<div class="col s4">
|
<div class="col s4">
|
||||||
<div class="card extension-selector hoverable service-color" data-service="tesseract-ocr-pipeline">
|
<div class="card extension-selector hoverable service-color" data-service="tesseract-ocr-pipeline">
|
||||||
<a href="{{ url_for('.tesseract_ocr_pipeline_models') }}" style="position: absolute; width: 100%; height: 100%;"></a>
|
<a href="{{ url_for('.tesseract_ocr_pipeline_models.tesseract_ocr_pipeline_models') }}" style="position: absolute; width: 100%; height: 100%;"></a>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<span class="card-title" data-service="tesseract-ocr-pipeline"><i class="nopaque-icons service-icons" data-service="tesseract-ocr-pipeline"></i>Tesseract OCR Pipeline Models</span>
|
<span class="card-title" data-service="tesseract-ocr-pipeline"><i class="nopaque-icons service-icons" data-service="tesseract-ocr-pipeline"></i>Tesseract OCR Pipeline Models</span>
|
||||||
<p>Here you can see and edit the models that you have created. You can also create new models.</p>
|
<p>Here you can see and edit the models that you have created. You can also create new models.</p>
|
||||||
@ -21,7 +20,7 @@
|
|||||||
|
|
||||||
<div class="col s4">
|
<div class="col s4">
|
||||||
<div class="card extension-selector hoverable service-color" data-service="spacy-nlp-pipeline">
|
<div class="card extension-selector hoverable service-color" data-service="spacy-nlp-pipeline">
|
||||||
<a href="{{ url_for('.spacy_nlp_pipeline_models') }}" style="position: absolute; width: 100%; height: 100%;"></a>
|
<a href="{{ url_for('.spacy_nlp_pipeline_models.spacy_nlp_pipeline_models') }}" style="position: absolute; width: 100%; height: 100%;"></a>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<span class="card-title"><i class="nopaque-icons service-icons" data-service="spacy-nlp-pipeline"></i>SpaCy NLP Pipeline Models</span>
|
<span class="card-title"><i class="nopaque-icons service-icons" data-service="spacy-nlp-pipeline"></i>SpaCy NLP Pipeline Models</span>
|
||||||
<p>Here you can see and edit the models that you have created. You can also create new models.</p>
|
<p>Here you can see and edit the models that you have created. You can also create new models.</p>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
{% extends "base.html.j2" %}
|
{% extends "base.html.j2" %}
|
||||||
{% import "materialize/wtf.html.j2" as wtf %}
|
{% import "materialize/wtf.html.j2" as wtf %}
|
||||||
{% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %}
|
|
||||||
|
|
||||||
{% block main_attribs %} class="service-scheme" data-service="spacy-nlp-pipeline"{% endblock main_attribs %}
|
{% block main_attribs %} class="service-scheme" data-service="spacy-nlp-pipeline"{% endblock main_attribs %}
|
||||||
|
|
@ -1,6 +1,5 @@
|
|||||||
{% extends "base.html.j2" %}
|
{% extends "base.html.j2" %}
|
||||||
{% import "materialize/wtf.html.j2" as wtf %}
|
{% import "materialize/wtf.html.j2" as wtf %}
|
||||||
{% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %}
|
|
||||||
|
|
||||||
{% block main_attribs %} class="service-scheme" data-service="spacy-nlp-pipeline"{% endblock main_attribs %}
|
{% block main_attribs %} class="service-scheme" data-service="spacy-nlp-pipeline"{% endblock main_attribs %}
|
||||||
|
|
@ -1,6 +1,5 @@
|
|||||||
{% extends "base.html.j2" %}
|
{% extends "base.html.j2" %}
|
||||||
{% import "materialize/wtf.html.j2" as wtf %}
|
{% import "materialize/wtf.html.j2" as wtf %}
|
||||||
{% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %}
|
|
||||||
|
|
||||||
{% block page_content %}
|
{% block page_content %}
|
||||||
<div class="container">
|
<div class="container">
|
@ -1,6 +1,5 @@
|
|||||||
{% extends "base.html.j2" %}
|
{% extends "base.html.j2" %}
|
||||||
{% import "materialize/wtf.html.j2" as wtf %}
|
{% import "materialize/wtf.html.j2" as wtf %}
|
||||||
{% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %}
|
|
||||||
|
|
||||||
{% block main_attribs %} class="service-scheme" data-service="tesseract-ocr-pipeline"{% endblock main_attribs %}
|
{% block main_attribs %} class="service-scheme" data-service="tesseract-ocr-pipeline"{% endblock main_attribs %}
|
||||||
|
|
@ -1,6 +1,5 @@
|
|||||||
{% extends "base.html.j2" %}
|
{% extends "base.html.j2" %}
|
||||||
{% import "materialize/wtf.html.j2" as wtf %}
|
{% import "materialize/wtf.html.j2" as wtf %}
|
||||||
{% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %}
|
|
||||||
|
|
||||||
{% block main_attribs %} class="service-scheme" data-service="tesseract-ocr-pipeline"{% endblock main_attribs %}
|
{% block main_attribs %} class="service-scheme" data-service="tesseract-ocr-pipeline"{% endblock main_attribs %}
|
||||||
|
|
@ -1,6 +1,5 @@
|
|||||||
{% extends "base.html.j2" %}
|
{% extends "base.html.j2" %}
|
||||||
{% import "materialize/wtf.html.j2" as wtf %}
|
{% import "materialize/wtf.html.j2" as wtf %}
|
||||||
{% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %}
|
|
||||||
|
|
||||||
{% block page_content %}
|
{% block page_content %}
|
||||||
<div class="container">
|
<div class="container">
|
Loading…
Reference in New Issue
Block a user