Contribution Package Spacy NLP

This commit is contained in:
Inga Kirschnick 2022-11-07 09:15:38 +01:00
parent 46ba14b923
commit 05340ea7ff
13 changed files with 564 additions and 57 deletions

View File

@ -5,6 +5,62 @@
publisher_url: 'https://github.com/explosion' publisher_url: 'https://github.com/explosion'
publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/de_core_news_md-3.4.0' publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/de_core_news_md-3.4.0'
publishing_year: 2022 publishing_year: 2022
pipeline_name: 'de_core_news_md'
version: '3.4.0' version: '3.4.0'
compatible_service_versions: compatible_service_versions:
- '0.1.0' - '0.1.0'
- title: 'en_core_web_md-3.4.1'
description: 'English pipeline optimized for CPU. Components: tok2vec, tagger, parser, senter, ner, attribute_ruler, lemmatizer.'
url: 'https://github.com/explosion/spacy-models/releases/download/en_core_web_md-3.4.1/en_core_web_md-3.4.1.tar.gz'
publisher: 'Explosion'
publisher_url: 'https://github.com/explosion'
publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/en_core_web_md-3.4.1'
publishing_year: 2022
pipeline_name: 'en_core_web_md'
version: '3.4.1'
compatible_service_versions:
- '0.1.0'
- title: 'uk_core_news_md-3.4.0'
description: 'Ukrainian pipeline optimized for CPU. Components: tok2vec, morphologizer, parser, senter, ner, attribute_ruler, lemmatizer.'
url: 'https://github.com/explosion/spacy-models/releases/download/uk_core_news_md-3.4.0/uk_core_news_md-3.4.0.tar.gz'
publisher: 'Explosion'
publisher_url: 'https://github.com/explosion'
publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/uk_core_news_md-3.4.0'
publishing_year: 2022
pipeline_name: 'uk_core_news_md'
version: '3.4.0'
compatible_service_versions:
- '0.1.0'
- title: 'zh_core_web_md-3.4.0'
description: 'Chinese pipeline optimized for CPU. Components: tok2vec, tagger, parser, senter, ner, attribute_ruler.'
url: 'https://github.com/explosion/spacy-models/releases/download/zh_core_web_md-3.4.0/zh_core_web_md-3.4.0.tar.gz'
publisher: 'Explosion'
publisher_url: 'https://github.com/explosion'
publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/zh_core_web_md-3.4.0'
publishing_year: 2022
pipeline_name: 'zh_core_web_md'
version: '3.4.0'
compatible_service_versions:
- '0.1.0'
- title: 'ru_core_news_md-3.4.0'
description: 'Russian pipeline optimized for CPU. Components: tok2vec, morphologizer, parser, senter, ner, attribute_ruler, lemmatizer.'
url: 'https://github.com/explosion/spacy-models/releases/download/ru_core_news_md-3.4.0/ru_core_news_md-3.4.0.tar.gz'
publisher: 'Explosion'
publisher_url: 'https://github.com/explosion'
publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/ru_core_news_md-3.4.0'
publishing_year: 2022
pipeline_name: 'ru_core_news_md'
version: '3.4.0'
compatible_service_versions:
- '0.1.0'
- title: 'la_core_cltk_sm-0.1.0'
description: 'Latin pipeline optimized for CPU. Components: tok2vec, morphologizer, parser, senter, ner, attribute_ruler, lemmatizer.'
url: 'https://github.com/diyclassics/latin-spacy-models/raw/main/la_core_cltk_sm/la_core_cltk_sm-0.1.0.tar.gz'
publisher: 'DIY Classics'
publisher_url: 'https://github.com/diyclassics/'
publishing_url: 'https://github.com/diyclassics/latin-spacy-models/tree/main/la_core_cltk_sm'
publishing_year: 2022
pipeline_name: 'la_core_cltk_sm'
version: '0.1.0'
compatible_service_versions:
- '0.1.0'

View File

@ -46,6 +46,18 @@ class CreateContributionBaseForm(FlaskForm):
) )
submit = SubmitField() submit = SubmitField()
class EditForm(CreateContributionBaseForm):
def prefill(self, model_file):
''' Pre-fill the form with data of an exististing corpus file '''
self.title.data = model_file.title
self.description.data = model_file.description
self.publisher.data = model_file.publisher
self.publishing_year.data = model_file.publishing_year
self.publisher_url.data = model_file.publisher_url
self.publishing_url.data = model_file.publishing_url
self.version.data = model_file.version
self.shared.data = model_file.shared
class TesseractOCRModelContributionForm(CreateContributionBaseForm): class TesseractOCRModelContributionForm(CreateContributionBaseForm):
tesseract_model_file = FileField( tesseract_model_file = FileField(
'File', 'File',
@ -67,16 +79,23 @@ class TesseractOCRModelContributionForm(CreateContributionBaseForm):
] ]
self.compatible_service_versions.default = '' self.compatible_service_versions.default = ''
class TesseractOCRModelEditForm(CreateContributionBaseForm): class SpacyNLPModelContributionForm(CreateContributionBaseForm):
def prefill(self, model_file): spacy_model_file = FileField(
''' Pre-fill the form with data of an exististing corpus file ''' 'File',
self.title.data = model_file.title validators=[FileRequired()]
self.description.data = model_file.description )
self.publisher.data = model_file.publisher compatible_service_versions = SelectMultipleField(
self.publishing_year.data = model_file.publishing_year 'Compatible service versions'
self.publisher_url.data = model_file.publisher_url )
self.publishing_url.data = model_file.publishing_url def validate_spacy(self, field):
self.version.data = model_file.version if field.data.mimetype != '.tar.gz':
self.shared.data = model_file.shared raise ValidationError('.tar.gz files only!')
def __init__(self, *args, **kwargs):
service_manifest = SERVICES['spacy-nlp-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 = ''

View File

@ -1,11 +1,11 @@
from flask import abort, current_app, flash, Markup, redirect, render_template, url_for from flask import abort, current_app, flash, Markup, render_template, url_for
from flask_login import login_required, current_user from flask_login import login_required, current_user
from threading import Thread from threading import Thread
from app import db from app import db
from app.decorators import admin_required, permission_required from app.decorators import admin_required, permission_required
from app.models import TesseractOCRPipelineModel, Permission from app.models import Permission, SpaCyNLPPipelineModel, TesseractOCRPipelineModel
from . import bp from . import bp
from .forms import TesseractOCRModelContributionForm, TesseractOCRModelEditForm from .forms import TesseractOCRModelContributionForm, EditForm, SpacyNLPModelContributionForm
@bp.before_request @bp.before_request
@ -22,20 +22,26 @@ def contributions():
tesseract_ocr_user_models = [ tesseract_ocr_user_models = [
x for x in current_user.tesseract_ocr_pipeline_models x for x in current_user.tesseract_ocr_pipeline_models
] ]
spacy_nlp_user_models = [
x for x in current_user.spacy_nlp_pipeline_models
]
spacy_models = SpaCyNLPPipelineModel.query.all()
print(spacy_models)
return render_template( return render_template(
'contributions/contribution_overview.html.j2', 'contributions/contribution_overview.html.j2',
tesseractOCRUserModels=tesseract_ocr_user_models, tesseract_ocr_user_models=tesseract_ocr_user_models,
spacy_nlp_user_models=spacy_nlp_user_models,
userId = current_user.hashid, userId = current_user.hashid,
title='Contribution Overview' title='Contribution Overview'
) )
@bp.route('/<hashid:tesseract_ocr_pipeline_model_id>', methods=['GET', 'POST']) @bp.route('/edit-tesseract-model/<hashid:tesseract_ocr_pipeline_model_id>', methods=['GET', 'POST'])
@login_required @login_required
def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id): def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id):
tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.query.get_or_404( tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.query.get_or_404(
tesseract_ocr_pipeline_model_id tesseract_ocr_pipeline_model_id
) )
form = TesseractOCRModelEditForm(prefix='tesseract-ocr-model-edit-form') form = EditForm(prefix='tesseract-ocr-model-edit-form')
if form.validate_on_submit(): if form.validate_on_submit():
if tesseract_ocr_pipeline_model.title != form.title.data: if tesseract_ocr_pipeline_model.title != form.title.data:
tesseract_ocr_pipeline_model.title = form.title.data tesseract_ocr_pipeline_model.title = form.title.data
@ -65,7 +71,7 @@ def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id):
title='Edit your Tesseract OCR model' title='Edit your Tesseract OCR model'
) )
@bp.route('/<hashid:tesseract_ocr_pipeline_model_id>', methods=['DELETE']) @bp.route('/edit-tesseract-model/<hashid:tesseract_ocr_pipeline_model_id>', methods=['DELETE'])
@login_required @login_required
def delete_tesseract_model(tesseract_ocr_pipeline_model_id): def delete_tesseract_model(tesseract_ocr_pipeline_model_id):
def _delete_tesseract_model(app, tesseract_ocr_pipeline_model_id): def _delete_tesseract_model(app, tesseract_ocr_pipeline_model_id):
@ -123,3 +129,95 @@ def add_tesseract_ocr_pipeline_model():
tesseract_ocr_pipeline_models=tesseract_ocr_pipeline_models, tesseract_ocr_pipeline_models=tesseract_ocr_pipeline_models,
title='Tesseract OCR Model Contribution' title='Tesseract OCR Model Contribution'
) )
@bp.route('/edit-spacy-model//<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 = EditForm(prefix='spacy-nlp-model-edit-form')
if form.validate_on_submit():
if spacy_nlp_pipeline_model.title != form.title.data:
spacy_nlp_pipeline_model.title = form.title.data
if spacy_nlp_pipeline_model.description != form.description.data:
spacy_nlp_pipeline_model.description = form.description.data
if spacy_nlp_pipeline_model.publisher != form.publisher.data:
spacy_nlp_pipeline_model.publisher = form.publisher.data
if spacy_nlp_pipeline_model.publishing_year != form.publishing_year.data:
spacy_nlp_pipeline_model.publishing_year = form.publishing_year.data
if spacy_nlp_pipeline_model.publisher_url != form.publisher_url.data:
spacy_nlp_pipeline_model.publisher_url = form.publisher_url.data
if spacy_nlp_pipeline_model.publishing_url != form.publishing_url.data:
spacy_nlp_pipeline_model.publishing_url = form.publishing_url.data
if spacy_nlp_pipeline_model.version != form.version.data:
spacy_nlp_pipeline_model.version = form.version.data
if spacy_nlp_pipeline_model.shared != form.shared.data:
spacy_nlp_pipeline_model.shared = form.shared.data
db.session.commit()
message = Markup(f'Model "<a href="contribute/{spacy_nlp_pipeline_model.hashid}">{spacy_nlp_pipeline_model.title}</a>" updated')
flash(message, category='corpus')
return {}, 201, {'Location': url_for('contributions.contributions')}
form.prefill(spacy_nlp_pipeline_model)
return render_template(
'contributions/spacy_nlp_pipeline_model.html.j2',
spacy_nlp_pipeline_model=spacy_nlp_pipeline_model,
form=form,
title='Edit your spaCy NLP model'
)
@bp.route('/edit-spacy-model/<hashid:spacy_nlp_pipeline_model_id>', methods=['DELETE'])
@login_required
def delete_spacy_model(spacy_nlp_pipeline_model_id):
def _delete_spacy_model(app, spacy_nlp_pipeline_model_id):
with app.app_context():
model = SpaCyNLPPipelineModel.query.get(spacy_nlp_pipeline_model_id)
model.delete()
db.session.commit()
model = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id)
if not (model.user == current_user or current_user.is_administrator()):
abort(403)
thread = Thread(
target=_delete_spacy_model,
args=(current_app._get_current_object(), spacy_nlp_pipeline_model_id)
)
thread.start()
return {}, 202
@bp.route('/add-spacy-nlp-pipeline-model', methods=['GET', 'POST'])
def add_spacy_nlp_pipeline_model():
form = SpacyNLPModelContributionForm(prefix='contribute-spacy-nlp-pipeline-model-form')
if form.is_submitted():
if not form.validate():
response = {'errors': form.errors}
return response, 400
try:
spacy_nlp_model = SpaCyNLPPipelineModel.create(
form.spacy_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,
shared=form.shared.data,
title=form.title.data,
version=form.version.data,
user=current_user
)
except OSError:
abort(500)
db.session.commit()
message = Markup(f'Model "{spacy_nlp_model.title}" created')
flash(message)
return {}, 201, {'Location': url_for('contributions.contributions')}
spacy_nlp_pipeline_models = [
x for x in SpaCyNLPPipelineModel.query.all()
]
return render_template(
'contributions/contribute_spacy_nlp_models.html.j2',
form=form,
spacy_nlp_pipeline_models=spacy_nlp_pipeline_models,
title='spaCy NLP Model Contribution'
)

View File

@ -520,6 +520,10 @@ class User(HashidMixin, UserMixin, db.Model):
x.hashid: x.to_json(relationships=True) x.hashid: x.to_json(relationships=True)
for x in self.tesseract_ocr_pipeline_models for x in self.tesseract_ocr_pipeline_models
} }
_json['spacy_nlp_pipeline_models'] = {
x.hashid: x.to_json(relationships=True)
for x in self.spacy_nlp_pipeline_models
}
return _json return _json
class TesseractOCRPipelineModel(FileMixin, HashidMixin, db.Model): class TesseractOCRPipelineModel(FileMixin, HashidMixin, db.Model):
@ -643,6 +647,7 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model):
publisher_url = db.Column(db.String(512)) publisher_url = db.Column(db.String(512))
publishing_url = db.Column(db.String(512)) publishing_url = db.Column(db.String(512))
publishing_year = db.Column(db.Integer) publishing_year = db.Column(db.Integer)
pipeline_name = db.Column(db.String(64))
shared = db.Column(db.Boolean, default=False) shared = db.Column(db.Boolean, default=False)
# Backrefs: user: User # Backrefs: user: User
@ -675,6 +680,7 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model):
model.shared = True model.shared = True
model.title = m['title'] model.title = m['title']
model.version = m['version'] model.version = m['version']
model.pipeline_name = m['pipeline_name']
continue continue
model = SpaCyNLPPipelineModel( model = SpaCyNLPPipelineModel(
compatible_service_versions=m['compatible_service_versions'], compatible_service_versions=m['compatible_service_versions'],
@ -686,7 +692,8 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model):
shared=True, shared=True,
title=m['title'], title=m['title'],
user=nopaque_user, user=nopaque_user,
version=m['version'] version=m['version'],
pipeline_name=m['pipeline_name']
) )
db.session.add(model) db.session.add(model)
db.session.flush(objects=[model]) db.session.flush(objects=[model])
@ -709,6 +716,13 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model):
pbar.close() pbar.close()
db.session.commit() db.session.commit()
def delete(self):
try:
os.remove(self.path)
except OSError as e:
current_app.logger.error(e)
db.session.delete(self)
def to_json(self, backrefs=False, relationships=False): def to_json(self, backrefs=False, relationships=False):
_json = { _json = {
'id': self.hashid, 'id': self.hashid,
@ -718,6 +732,7 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model):
'publisher_url': self.publisher_url, 'publisher_url': self.publisher_url,
'publishing_url': self.publishing_url, 'publishing_url': self.publishing_url,
'publishing_year': self.publishing_year, 'publishing_year': self.publishing_year,
'pipeline_name': self.pipeline_name,
'shared': self.shared, 'shared': self.shared,
'title': self.title, 'title': self.title,
**self.file_mixin_to_json() **self.file_mixin_to_json()

View File

@ -0,0 +1,76 @@
class SpacyNLPModelList {
constructor () {
this.elements = {
spacyNLPModelList: document.querySelector('#spacy-nlp-model-list'),
deleteButtons: document.querySelectorAll('.delete-spacy-model-button'),
editButtons: document.querySelectorAll('.edit-spacy-model-button'),
}
}
init () {
let userId = this.elements.spacyNLPModelList.dataset.userId;
for (let deleteButton of this.elements.deleteButtons) {
deleteButton.addEventListener('click', () => {this.deleteModel(deleteButton, userId);});
}
for (let editButton of this.elements.editButtons) {
editButton.addEventListener('click', () => {this.editModel(editButton);});
}
}
deleteModel(deleteButton, userId) {
return new Promise((resolve, reject) => {
let modelId = deleteButton.dataset.modelId;
let model = app.data.users[userId].spacy_nlp_pipeline_models[modelId];
let modalElement = Utils.elementFromString(
`
<div class="modal">
<div class="modal-content">
<h4>Confirm job deletion</h4>
<p>Do you really want to delete <b>${model.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"]');
confirmElement.addEventListener('click', (event) => {
let modelTitle = model.title;
fetch(`/contributions/edit-spacy-model/${modelId}`, {method: 'DELETE'})
.then(
(response) => {
app.flash(`Model "${modelTitle}" marked for deletion`, 'corpus');
resolve(response);
},
(response) => {
if (response.status === 403) {app.flash('Forbidden', 'error');}
if (response.status === 404) {app.flash('Not Found', 'error');}
reject(response);
}
);
});
modal.open();
});
}
editModel(editButton) {
window.location.href = `/contributions/edit-spacy-model/${editButton.dataset.modelId}`;
}
}

View File

@ -25,7 +25,6 @@ class TesseractOCRModelList {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let modelId = deleteButton.dataset.modelId; let modelId = deleteButton.dataset.modelId;
let model = app.data.users[userId].tesseract_ocr_pipeline_models[modelId]; let model = app.data.users[userId].tesseract_ocr_pipeline_models[modelId];
let modalElement = Utils.elementFromString( let modalElement = Utils.elementFromString(
` `
<div class="modal"> <div class="modal">
@ -54,7 +53,7 @@ class TesseractOCRModelList {
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 modelTitle = model.title; let modelTitle = model.title;
fetch(`/contributions/${modelId}`, {method: 'DELETE'}) fetch(`/contributions/edit-tesseract-model/${modelId}`, {method: 'DELETE'})
.then( .then(
(response) => { (response) => {
app.flash(`Model "${modelTitle}" marked for deletion`, 'corpus'); app.flash(`Model "${modelTitle}" marked for deletion`, 'corpus');
@ -72,6 +71,6 @@ class TesseractOCRModelList {
} }
editModel(editButton) { editModel(editButton) {
window.location.href = `/contributions/${editButton.dataset.modelId}`; window.location.href = `/contributions/edit-tesseract-model/${editButton.dataset.modelId}`;
} }
} }

View File

@ -25,6 +25,7 @@
'js/RessourceLists/JobInputList.js', 'js/RessourceLists/JobInputList.js',
'js/RessourceLists/JobResultList.js', 'js/RessourceLists/JobResultList.js',
'js/RessourceLists/QueryResultList.js', 'js/RessourceLists/QueryResultList.js',
'js/RessourceLists/SpacyNLPModelList.js',
'js/RessourceLists/TesseractOCRModelList.js', 'js/RessourceLists/TesseractOCRModelList.js',
'js/RessourceLists/UserList.js' 'js/RessourceLists/UserList.js'
%} %}

View File

@ -14,5 +14,18 @@
<li class="tab"><a href="{{ url_for('.contributions', tesseract_ocr_pipeline_model_id=nn) }}" target="_self">Contributions Overview</a></li> <li class="tab"><a href="{{ url_for('.contributions', tesseract_ocr_pipeline_model_id=nn) }}" target="_self">Contributions Overview</a></li>
<li class="tab disabled"><i class="material-icons">navigate_next</i></li> <li class="tab disabled"><i class="material-icons">navigate_next</i></li>
<li class="tab"><a class="active" href="{{ url_for('.add_tesseract_ocr_pipeline_model') }}" target="_self">{{ title }}</a></li> <li class="tab"><a class="active" href="{{ url_for('.add_tesseract_ocr_pipeline_model') }}" target="_self">{{ title }}</a></li>
{% elif request.path == url_for('.spacy_nlp_pipeline_model', spacy_nlp_pipeline_model_id=spacy_nlp_pipeline_model.id) %}
<li class="tab"><a href="{{ url_for('.contributions') }}" target="_self">Contributions Overview</a></li>
<li class="tab disabled"><i class="material-icons">navigate_next</i></li>
<li class="tab">
<a class="active" href="{{ url_for('.spacy_nlp_pipeline_model', spacy_nlp_pipeline_model_id=spacy_nlp_pipeline_model.hashid) }}" target="_self">
Edit {{ spacy_nlp_pipeline_model.title }}
</a>
</li>
{% elif request.path == url_for('.add_spacy_nlp_pipeline_model, spacy_nlp_pipeline_model=nn') %}
<li class="tab"><a href="{{ url_for('.contributions', spacy_nlp_pipeline_model_id=nn) }}" target="_self">Contributions Overview</a></li>
<li class="tab disabled"><i class="material-icons">navigate_next</i></li>
<li class="tab"><a class="active" href="{{ url_for('.add_spacy_nlp_pipeline_model') }}" target="_self">{{ title }}</a></li>
{% endif %} {% endif %}
{% endset %} {% endset %}

View File

@ -1,32 +0,0 @@
{% extends "base.html.j2" %}
{% import "materialize/wtf.html.j2" as wtf %}
{% block page_content %}
<div class="container">
<div class="row">
<div class="col s12 m8 offset-m2">
<h1 id="title">{{ title }}</h1>
<p>
In order to add a new model, please fill in the form below.
</p>
<form method="POST">
<div class="card-panel">
{{ form.hidden_tag() }}
{{ wtf.render_field(form.title) }}
{{ wtf.render_field(form.description) }}
{{ wtf.render_field(form.publisher) }}
{{ wtf.render_field(form.publisher_url) }}
{{ wtf.render_field(form.publishing_url) }}
{{ wtf.render_field(form.publishing_year) }}
{{ wtf.render_field(form.shared) }}
{{ wtf.render_field(form.version) }}
{{ wtf.render_field(form.compatible_service_versions) }}
{{ wtf.render_field(form.submit, class_='width-100', material_icon='send') }}
</div>
</form>
</div>
</div>
{% endblock page_content %}

View File

@ -0,0 +1,124 @@
{% extends "base.html.j2" %}
{% 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 page_content %}
<div class="container">
<div class="row">
<div class="col s12">
<h1 id="title">{{ title }}</h1>
</div>
<div class="col s12 m3 push-m9">
<div class="center-align">
<p class="hide-on-small-only">&nbsp;</p>
<p class="hide-on-small-only">&nbsp;</p>
<a class="btn-floating btn-large btn-scale-x2 waves-effect waves-light">
<i class="nopaque-icons service-color darken service-icon" data-service="spacy-nlp-pipeline"></i>
</a>
</div>
</div>
<div class="col s12 m9 pull-m3">
<div class="card service-color-border border-darken" data-service="spacy-nlp-pipeline" style="border-top: 10px solid;">
<div class="card-content">
<div class="row">
<div class="col s12">
<div class="card-panel z-depth-0">
<span class="card-title"><i class="left material-icons">layers</i>spaCy NLP Models</span>
<p>You can add more Tesseract OCR models using the form below. They will automatically appear in the list of usable models.</p>
<p><a href="">Edit already uploaded models</a></p>
<p><a class="modal-trigger" href="#models-modal">Information about the already existing models.</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col s12">
<h2>Add a model</h2>
<div class="card">
<form class="create-contribution-form" enctype="multipart/form-data" method="POST">
<div class="card-content">
{{ form.hidden_tag() }}
<div class="row">
<div class="col s12 l5">
{{ wtf.render_field(form.spacy_model_file, accept='.tar.gz', placeholder='Choose a .tar.gz file') }}
</div>
<div class="col s12 l7">
{{ wtf.render_field(form.title, material_icon='title') }}
</div>
<div class="col s12">
{{ wtf.render_field(form.description, material_icon='description') }}
</div>
<div class="col s12 l6">
{{ wtf.render_field(form.publisher, material_icon='account_balance') }}
</div>
<div class="col s12 l6">
{{ wtf.render_field(form.publishing_year, material_icon='calendar_month') }}
</div>
<div class="col s12">
{{ wtf.render_field(form.publisher_url, material_icon='link') }}
</div>
<div class="col s12">
{{ wtf.render_field(form.publishing_url, material_icon='link') }}
</div>
<div class="col s12 l10">
{{ wtf.render_field(form.version, material_icon='apps') }}
</div>
<div class="col s12 l6">
{{ wtf.render_field(form.compatible_service_versions) }}
</div>
<div class="col s12 l6 right-align" style="padding-right:20px;">
<p></p>
<br>
{{ wtf.render_field(form.shared) }}
</div>
</div>
</div>
<div class="card-action right-align">
{{ wtf.render_field(form.submit, material_icon='send') }}
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock page_content %}
{% block modals %}
{{ super() }}
<div id="models-modal" class="modal">
<div class="modal-content">
<h4>spaCy NLP Pipeline models</h4>
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Biblio</th>
</tr>
</thead>
<tbody>
{% for m in spacy_nlp_pipeline_models %}
<tr id="spacy-nlp-pipeline-model-{{ m.hashid }}">
<td>{{ m.title }}</td>
{% if m.description == '' %}
<td>Description is not available.</td>
{% else %}
<td>{{ m.description }}</td>
{% endif %}
<td><a href="{{ m.publisher_url }}">{{ m.publisher }}</a> ({{ m.publishing_year }}), {{ m.title }} {{ m.version}}, <a href="{{ m.publishing_url }}">{{ m.publishing_url }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="modal-footer">
<a href="#!" class="modal-close waves-effect waves-light btn">Close</a>
</div>
</div>
{% endblock modals %}

View File

@ -28,8 +28,8 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% if tesseractOCRUserModels|length > 0 %} {% if tesseract_ocr_user_models|length > 0 %}
{% for m in tesseractOCRUserModels %} {% for m in tesseract_ocr_user_models %}
<tr id="tesseract-ocr-pipeline-model-{{ m.hashid }}"> <tr id="tesseract-ocr-pipeline-model-{{ m.hashid }}">
<td>{{ m.title }}</td> <td>{{ m.title }}</td>
{% if m.description == '' %} {% if m.description == '' %}
@ -61,6 +61,58 @@
</div> </div>
</div> </div>
{# spaCy NLP Models #}
<div>
<h3>My spaCy NLP Pipeline Models</h3>
<p>Here you can see and edit the models that you have created. You can also create new models.</p>
<div class="row">
<div class="col s12">
<div class="card">
<div class="card-content">
<div id="spacy-nlp-model-list" data-user-id="{{ userId }}" data-user-models="{{ spacy_nlp_user_models }}">
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Biblio</th>
<th></th>
</tr>
</thead>
<tbody>
{% if spacy_nlp_user_models|length > 0 %}
{% for m in spacy_nlp_user_models %}
<tr id="spacy_nlp-pipeline-model-{{ m.hashid }}">
<td>{{ m.title }}</td>
{% if m.description == '' %}
<td>Description is not available.</td>
{% else %}
<td>{{ m.description }}</td>
{% endif %}
<td><a href="{{ m.publisher_url }}">{{ m.publisher }}</a> ({{ m.publishing_year }}), {{ m.title }} {{ m.version}}, <a href="{{ m.publishing_url }}">{{ m.publishing_url }}</a></td>
<td class="right-align">
<a class="delete-spacy-model-button btn-floating red waves-effect waves-light" data-model-id="{{ m.hashid }}"><i class="material-icons">delete</i></a>
<a class="edit-spacy-model-button btn-floating service-color darken waves-effect waves-light" data-model-id="{{ m.hashid }}"><i class="material-icons">edit</i></a>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="4">No models available.</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
<div class="card-action right-align">
<a href="{{ url_for('contributions.add_spacy_nlp_pipeline_model') }}" class="btn waves-effect waves-light"><i class="material-icons left">add</i>Add model file</a>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -71,5 +123,7 @@
<script> <script>
const tesseractOCRModelList = new TesseractOCRModelList(); const tesseractOCRModelList = new TesseractOCRModelList();
tesseractOCRModelList.init(); tesseractOCRModelList.init();
const spacyNLPModelList = new SpacyNLPModelList();
spacyNLPModelList.init();
</script> </script>
{% endblock scripts %} {% endblock scripts %}

View File

@ -0,0 +1,56 @@
{% extends "base.html.j2" %}
{% 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 page_content %}
<div class="container">
<div class="row">
<div class="col s12">
<h1 id="title">{{ title }}</h1>
</div>
<div class="col s12">
<div class="card">
<form class="create-contribution-form" enctype="multipart/form-data" method="POST">
<div class="card-content">
{{ form.hidden_tag() }}
<div class="row">
<div class="col s12 l7">
{{ wtf.render_field(form.title, material_icon='title') }}
</div>
<div class="col s12">
{{ wtf.render_field(form.description, material_icon='description') }}
</div>
<div class="col s12 l6">
{{ wtf.render_field(form.publisher, material_icon='account_balance') }}
</div>
<div class="col s12 l6">
{{ wtf.render_field(form.publishing_year, material_icon='calendar_month') }}
</div>
<div class="col s12">
{{ wtf.render_field(form.publisher_url, material_icon='link') }}
</div>
<div class="col s12">
{{ wtf.render_field(form.publishing_url, material_icon='link') }}
</div>
<div class="col s12 l10">
{{ wtf.render_field(form.version, material_icon='apps') }}
</div>
<div class="col s12 l6 right-align" style="padding-right:20px;">
<p></p>
<br>
{{ wtf.render_field(form.shared) }}
</div>
</div>
</div>
<div class="card-action right-align">
{{ wtf.render_field(form.submit, material_icon='send') }}
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock page_content %}

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: 721829b5dd25
Revises: 31dd42e5ea6f
Create Date: 2022-11-04 13:58:13.008301
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '721829b5dd25'
down_revision = '31dd42e5ea6f'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('spacy_nlp_pipeline_models', sa.Column('pipeline_name', sa.String(length=64), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('spacy_nlp_pipeline_models', 'pipeline_name')
# ### end Alembic commands ###