From cd72614c0fd0226a2285d5a50918068d50667026 Mon Sep 17 00:00:00 2001 From: Patrick Jentsch <p.jentsch@uni-bielefeld.de> Date: Tue, 15 Nov 2022 15:11:16 +0100 Subject: [PATCH] Contributions update revised --- app/contributions/forms.py | 61 ++++--- app/contributions/routes.py | 162 +++++++++--------- app/models.py | 2 + app/services/services.yml | 4 +- app/static/js/RessourceLists/RessourceList.js | 2 + .../js/RessourceLists/SpacyNLPModelList.js | 76 -------- .../SpacyNLPPipelineModelList.js | 99 +++++++++++ .../RessourceLists/TesseractOCRModelList.js | 76 -------- .../TesseractOCRPipelineModelList.js | 99 +++++++++++ app/static/js/Utils.js | 100 ++++++++++- app/templates/_scripts.html.j2 | 4 +- .../contributions/_breadcrumbs.html.j2 | 8 +- .../contribution_overview.html.j2 | 129 -------------- .../contributions/contributions.html.j2 | 51 ++++++ ...> create_spacy_nlp_pipeline_model.html.j2} | 41 +---- ...eate_tesseract_ocr_pipeline_model.html.j2} | 7 +- .../spacy_nlp_pipeline_model.html.j2 | 2 +- .../tesseract_ocr_pipeline_model.html.j2 | 2 +- 18 files changed, 481 insertions(+), 444 deletions(-) delete mode 100644 app/static/js/RessourceLists/SpacyNLPModelList.js create mode 100644 app/static/js/RessourceLists/SpacyNLPPipelineModelList.js delete mode 100644 app/static/js/RessourceLists/TesseractOCRModelList.js create mode 100644 app/static/js/RessourceLists/TesseractOCRPipelineModelList.js delete mode 100644 app/templates/contributions/contribution_overview.html.j2 create mode 100644 app/templates/contributions/contributions.html.j2 rename app/templates/contributions/{contribute_spacy_nlp_models.html.j2 => create_spacy_nlp_pipeline_model.html.j2} (70%) rename app/templates/contributions/{contribute_tesseract_ocr_models.html.j2 => create_tesseract_ocr_pipeline_model.html.j2} (91%) diff --git a/app/contributions/forms.py b/app/contributions/forms.py index 8eb44842..04030fd0 100644 --- a/app/contributions/forms.py +++ b/app/contributions/forms.py @@ -12,6 +12,7 @@ from wtforms import ( from wtforms.validators import InputRequired, Length from app.services import SERVICES + class CreateContributionBaseForm(FlaskForm): title = StringField( 'Title', @@ -46,31 +47,8 @@ class CreateContributionBaseForm(FlaskForm): ) 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 EditTesseractOCRModelForm(EditForm): - pass - -class EditSpaCyNLPPipelineModelForm(EditForm): - pipeline_name = StringField( - 'Pipeline name', - validators=[InputRequired(), Length(max=64)] - ) - def prefill(self, model_file): - super().prefill(model_file) - self.pipeline_name.data = model_file.pipeline_name -class TesseractOCRModelContributionForm(CreateContributionBaseForm): +class CreateTesseractOCRPipelineModelForm(CreateContributionBaseForm): tesseract_model_file = FileField( 'File', validators=[FileRequired()] @@ -78,6 +56,7 @@ class TesseractOCRModelContributionForm(CreateContributionBaseForm): compatible_service_versions = SelectMultipleField( 'Compatible service versions' ) + def validate_tesseract_model_file(self, field): current_app.logger.warning(field.data.filename) if not field.data.filename.lower().endswith('.traineddata'): @@ -92,7 +71,8 @@ class TesseractOCRModelContributionForm(CreateContributionBaseForm): ] self.compatible_service_versions.default = '' -class SpacyNLPModelContributionForm(CreateContributionBaseForm): + +class CreateSpaCyNLPPipelineModelForm(CreateContributionBaseForm): spacy_model_file = FileField( 'File', validators=[FileRequired()] @@ -104,16 +84,45 @@ class SpacyNLPModelContributionForm(CreateContributionBaseForm): 'Pipeline name', validators=[InputRequired(), Length(max=64)] ) + def validate_spacy_model_file(self, field): current_app.logger.warning(field.data.filename) if not field.data.filename.lower().endswith('.tar.gz'): raise ValidationError('.tar.gz files only!') def __init__(self, *args, **kwargs): - service_manifest = SERVICES['spacy-nlp-pipeline'] 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(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 EditTesseractOCRPipelineModelForm(EditContributionBaseForm): + pass + + +class EditSpaCyNLPPipelineModelForm(EditContributionBaseForm): + pipeline_name = StringField( + 'Pipeline name', + validators=[InputRequired(), Length(max=64)] + ) + + def prefill(self, model_file): + super().prefill(model_file) + self.pipeline_name.data = model_file.pipeline_name diff --git a/app/contributions/routes.py b/app/contributions/routes.py index 33355b2d..121fc101 100644 --- a/app/contributions/routes.py +++ b/app/contributions/routes.py @@ -2,10 +2,19 @@ from flask import abort, current_app, flash, Markup, render_template, url_for from flask_login import login_required, current_user from threading import Thread from app import db -from app.decorators import admin_required, permission_required -from app.models import Permission, SpaCyNLPPipelineModel, TesseractOCRPipelineModel +from app.decorators import permission_required +from app.models import ( + Permission, + SpaCyNLPPipelineModel, + TesseractOCRPipelineModel +) from . import bp -from .forms import TesseractOCRModelContributionForm, EditSpaCyNLPPipelineModelForm, EditTesseractOCRModelForm, SpacyNLPModelContributionForm +from .forms import ( + CreateSpaCyNLPPipelineModelForm, + CreateTesseractOCRPipelineModelForm, + EditSpaCyNLPPipelineModelForm, + EditTesseractOCRPipelineModelForm +) @bp.before_request @@ -16,30 +25,17 @@ def before_request(): @bp.route('/') -@login_required -@admin_required def contributions(): - tesseract_ocr_user_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 - ] return render_template( - 'contributions/contribution_overview.html.j2', - tesseract_ocr_user_models=tesseract_ocr_user_models, - spacy_nlp_user_models=spacy_nlp_user_models, - userId = current_user.hashid, - title='Contribution Overview' + 'contributions/contributions.html.j2', + title='Contributions' ) -@bp.route('/edit-tesseract-model/<hashid:tesseract_ocr_pipeline_model_id>', methods=['GET', 'POST']) -@login_required + +@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 = EditTesseractOCRModelForm(prefix='tesseract-ocr-model-edit-form') + tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id) + form = EditTesseractOCRPipelineModelForm(prefix='edit-tesseract-ocr-pipeline-model-form') if form.validate_on_submit(): if tesseract_ocr_pipeline_model.title != form.title.data: tesseract_ocr_pipeline_model.title = form.title.data @@ -58,47 +54,50 @@ def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id): if tesseract_ocr_pipeline_model.shared != form.shared.data: tesseract_ocr_pipeline_model.shared = form.shared.data db.session.commit() - message = Markup(f'Model "<a href="contribute/{tesseract_ocr_pipeline_model.hashid}">{tesseract_ocr_pipeline_model.title}</a>" updated') - flash(message, category='corpus') - return {}, 201, {'Location': url_for('contributions.contributions')} + 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>" updated') + flash(message) + return {}, 201, {'Location': tesseract_ocr_pipeline_model_url} form.prefill(tesseract_ocr_pipeline_model) return render_template( 'contributions/tesseract_ocr_pipeline_model.html.j2', - tesseract_ocr_pipeline_model=tesseract_ocr_pipeline_model, form=form, - title='Edit your Tesseract OCR model' + tesseract_ocr_pipeline_model=tesseract_ocr_pipeline_model, + title='Edit Tesseract OCR Pipeline Model' ) -@bp.route('/edit-tesseract-model/<hashid:tesseract_ocr_pipeline_model_id>', methods=['DELETE']) -@login_required + +@bp.route('/tesseract_ocr_pipeline_models/<hashid:tesseract_ocr_pipeline_model_id>', methods=['DELETE']) def delete_tesseract_model(tesseract_ocr_pipeline_model_id): - def _delete_tesseract_model(app, tesseract_ocr_pipeline_model_id): + def _delete_tesseract_ocr_pipeline_model(app, tesseract_ocr_pipeline_model_id): with app.app_context(): - model = TesseractOCRPipelineModel.query.get(tesseract_ocr_pipeline_model_id) - model.delete() + tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.query.get(tesseract_ocr_pipeline_model_id) + tesseract_ocr_pipeline_model.delete() db.session.commit() - - model = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id) - if not (model.user == current_user or current_user.is_administrator()): + + tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id) + if not (tesseract_ocr_pipeline_model.user == current_user or current_user.is_administrator()): abort(403) thread = Thread( - target=_delete_tesseract_model, + target=_delete_tesseract_ocr_pipeline_model, args=(current_app._get_current_object(), tesseract_ocr_pipeline_model_id) ) thread.start() return {}, 202 -@bp.route('/add-tesseract-ocr-pipeline-model', methods=['GET', 'POST']) -def add_tesseract_ocr_pipeline_model(): - form = TesseractOCRModelContributionForm( - prefix='contribute-tesseract-ocr-pipeline-model-form' - ) + +@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_model = TesseractOCRPipelineModel.create( + tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.create( form.tesseract_model_file.data, compatible_service_versions=form.compatible_service_versions.data, description=form.description.data, @@ -114,27 +113,24 @@ def add_tesseract_ocr_pipeline_model(): except OSError: abort(500) db.session.commit() - message = Markup(f'Model "{tesseract_ocr_model.title}" created') + 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': url_for('contributions.contributions')} - tesseract_ocr_pipeline_models = [ - x for x in TesseractOCRPipelineModel.query.all() - ] - + return {}, 201, {'Location': tesseract_ocr_pipeline_model_url} return render_template( - 'contributions/contribute_tesseract_ocr_models.html.j2', + 'contributions/create_tesseract_ocr_pipeline_model.html.j2', form=form, - tesseract_ocr_pipeline_models=tesseract_ocr_pipeline_models, - title='Tesseract OCR Model Contribution' + title='Create Tesseract OCR Pipeline Model' ) -@bp.route('/edit-spacy-model//<hashid:spacy_nlp_pipeline_model_id>', methods=['GET', 'POST']) -@login_required + +@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(prefix='spacy-nlp-model-edit-form') + spacy_nlp_pipeline_model = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id) + form = EditSpaCyNLPPipelineModelForm(prefix='edit-spacy-nlp-pipeline-model-form') if form.validate_on_submit(): if spacy_nlp_pipeline_model.title != form.title.data: spacy_nlp_pipeline_model.title = form.title.data @@ -154,30 +150,33 @@ def spacy_nlp_pipeline_model(spacy_nlp_pipeline_model_id): 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 + current_app.logger.warning(db.session.dirty) 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')} - print(spacy_nlp_pipeline_model.to_json()) + 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>" updated') + flash(message) + return {}, 201, {'Location': url_for('.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' + spacy_nlp_pipeline_model=spacy_nlp_pipeline_model, + title=f'{spacy_nlp_pipeline_model.title} [{spacy_nlp_pipeline_model.version}]' ) -@bp.route('/edit-spacy-model/<hashid:spacy_nlp_pipeline_model_id>', methods=['DELETE']) -@login_required +@bp.route('/spacy-nlp-pipeline-models/<hashid:spacy_nlp_pipeline_model_id>', methods=['DELETE']) 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() + spacy_nlp_pipeline_model = SpaCyNLPPipelineModel.query.get(spacy_nlp_pipeline_model_id) + spacy_nlp_pipeline_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()): + spacy_nlp_pipeline_model = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id) + if not (spacy_nlp_pipeline_model.user == current_user or current_user.is_administrator()): abort(403) thread = Thread( target=_delete_spacy_model, @@ -186,15 +185,16 @@ def delete_spacy_model(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') + +@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_model = SpaCyNLPPipelineModel.create( + spacy_nlp_pipeline_model = SpaCyNLPPipelineModel.create( form.spacy_model_file.data, compatible_service_versions=form.compatible_service_versions.data, description=form.description.data, @@ -211,15 +211,15 @@ def add_spacy_nlp_pipeline_model(): except OSError: abort(500) db.session.commit() - message = Markup(f'Model "{spacy_nlp_model.title}" created') + 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': url_for('contributions.contributions')} - spacy_nlp_pipeline_models = [ - x for x in SpaCyNLPPipelineModel.query.all() - ] + return {}, 201, {'Location': spacy_nlp_pipeline_model_url} return render_template( - 'contributions/contribute_spacy_nlp_models.html.j2', + 'contributions/create_spacy_nlp_pipeline_model.html.j2', form=form, - spacy_nlp_pipeline_models=spacy_nlp_pipeline_models, - title='spaCy NLP Model Contribution' + title='Create SpaCy NLP Pipeline Model' ) diff --git a/app/models.py b/app/models.py index 93a23461..3bf7f8a7 100644 --- a/app/models.py +++ b/app/models.py @@ -625,6 +625,7 @@ class TesseractOCRPipelineModel(FileMixin, HashidMixin, db.Model): 'publishing_year': self.publishing_year, 'shared': self.shared, 'title': self.title, + 'version': self.version, **self.file_mixin_to_json() } if backrefs: @@ -735,6 +736,7 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model): 'pipeline_name': self.pipeline_name, 'shared': self.shared, 'title': self.title, + 'version': self.version, **self.file_mixin_to_json() } if backrefs: diff --git a/app/services/services.yml b/app/services/services.yml index c9d61e08..9d99d688 100644 --- a/app/services/services.yml +++ b/app/services/services.yml @@ -1,6 +1,6 @@ # TODO: This could also be done via GitLab/GitHub APIs file-setup-pipeline: - name: 'File setup pipeline' + name: 'File Setup Pipeline' publisher: 'Bielefeld University - CRC 1288 - INF' latest_version: '0.1.0' versions: @@ -38,7 +38,7 @@ transkribus-htr-pipeline: publishing_year: 2022 url: 'https://gitlab.ub.uni-bielefeld.de/sfb1288inf/transkribus-htr-pipeline/-/releases/v0.1.1' spacy-nlp-pipeline: - name: 'spaCy NLP Pipeline' + name: 'SpaCy NLP Pipeline' publisher: 'Bielefeld University - CRC 1288 - INF' latest_version: '0.1.0' versions: diff --git a/app/static/js/RessourceLists/RessourceList.js b/app/static/js/RessourceLists/RessourceList.js index 824db3d1..a2242054 100644 --- a/app/static/js/RessourceLists/RessourceList.js +++ b/app/static/js/RessourceLists/RessourceList.js @@ -10,6 +10,8 @@ class RessourceList { JobList.autoInit(); JobInputList.autoInit(); JobResultList.autoInit(); + SpacyNLPPipelineModelList.autoInit(); + TesseractOCRPipelineModelList.autoInit(); QueryResultList.autoInit(); UserList.autoInit(); } diff --git a/app/static/js/RessourceLists/SpacyNLPModelList.js b/app/static/js/RessourceLists/SpacyNLPModelList.js deleted file mode 100644 index 0e20191b..00000000 --- a/app/static/js/RessourceLists/SpacyNLPModelList.js +++ /dev/null @@ -1,76 +0,0 @@ -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}`; - } -} diff --git a/app/static/js/RessourceLists/SpacyNLPPipelineModelList.js b/app/static/js/RessourceLists/SpacyNLPPipelineModelList.js new file mode 100644 index 00000000..fcc68a0d --- /dev/null +++ b/app/static/js/RessourceLists/SpacyNLPPipelineModelList.js @@ -0,0 +1,99 @@ +class SpacyNLPPipelineModelList extends RessourceList { + static autoInit() { + for (let spaCyNLPPipelineModelListElement of document.querySelectorAll('.spacy-nlp-pipeline-model-list:not(.no-autoinit)')) { + new SpacyNLPPipelineModelList(spaCyNLPPipelineModelListElement); + } + } + + static options = { + initialHtmlGenerator: (id) => { + return ` + <div class="input-field"> + <i class="material-icons prefix">search</i> + <input id="${id}-search" class="search" type="search"></input> + <label for="${id}-search">Search SpaCy NLP Pipeline Model</label> + </div> + <table> + <thead> + <tr> + <th>Title</th> + <th>Description</th> + <th>Biblio</th> + <th></th> + </tr> + </thead> + <tbody class="list"></tbody> + </table> + <ul class="pagination"></ul> + `.trim(); + }, + item: ` + <tr class="clickable hoverable"> + <td><span class="title"></span></td> + <td><span class="description"></span></td> + <td><a class="publisher-url"><span class="publisher"></span></a> (<span class="publishing-year"></span>), <span class="title-2"></span> <span class="version"></span>, <a class="publishing-url"><span class="publishing-url-2"></span></a></td> + <td class="right-align"> + <a class="action-button btn-floating red waves-effect waves-light" data-action="delete-request"><i class="material-icons">delete</i></a> + <a class="action-button btn-floating service-color darken waves-effect waves-light service-2" data-action="view"><i class="material-icons">send</i></a> + </td> + </tr> + `.trim(), + ressourceMapper: (spaCyNLPPipelineModel) => { + return { + 'id': spaCyNLPPipelineModel.id, + 'creation-date': spaCyNLPPipelineModel.creation_date, + 'description': spaCyNLPPipelineModel.description, + 'publisher': spaCyNLPPipelineModel.publisher, + 'publisher-url': spaCyNLPPipelineModel.publisher_url, + 'publishing-url': spaCyNLPPipelineModel.publishing_url, + 'publishing-url-2': spaCyNLPPipelineModel.publishing_url, + 'publishing-year': spaCyNLPPipelineModel.publishing_year, + 'title': spaCyNLPPipelineModel.title, + 'title-2': spaCyNLPPipelineModel.title, + 'version': spaCyNLPPipelineModel.version + }; + }, + sortArgs: ['creation-date', {order: 'desc'}], + valueNames: [ + {data: ['id']}, + {data: ['creation-date']}, + {name: 'publisher-url', attr: 'href'}, + {name: 'publishing-url', attr: 'href'}, + 'description', + 'publisher', + 'publishing-url-2', + 'publishing-year', + 'title', + 'title-2', + 'version' + ] + }; + + constructor(listElement, options = {}) { + super(listElement, {...SpacyNLPPipelineModelList.options, ...options}); + } + + init (user) { + this._init(user.spacy_nlp_pipeline_models); + } + + onClick(event) { + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; + let spaCyNLPPipelineModelElement = event.target.closest('tr'); + let spaCyNLPPipelineModelId = spaCyNLPPipelineModelElement.dataset.id; + switch (action) { + case 'delete-request': { + Utils.deleteSpaCyNLPPipelineModelRequest(this.userId, spaCyNLPPipelineModelId); + break; + } + case 'view': { + window.location.href = `/contributions/spacy-nlp-pipeline-models/${spaCyNLPPipelineModelId}`; + break; + } + default: { + break; + } + } + } +} diff --git a/app/static/js/RessourceLists/TesseractOCRModelList.js b/app/static/js/RessourceLists/TesseractOCRModelList.js deleted file mode 100644 index 782f5d7e..00000000 --- a/app/static/js/RessourceLists/TesseractOCRModelList.js +++ /dev/null @@ -1,76 +0,0 @@ -class TesseractOCRModelList { - constructor () { - - this.elements = { - tesseractOCRModelList: document.querySelector('#tesseract-ocr-model-list'), - deleteButtons: document.querySelectorAll('.delete-button'), - editButtons: document.querySelectorAll('.edit-button'), - - } - } - - init () { - let userId = this.elements.tesseractOCRModelList.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].tesseract_ocr_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? 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-tesseract-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-tesseract-model/${editButton.dataset.modelId}`; - } -} diff --git a/app/static/js/RessourceLists/TesseractOCRPipelineModelList.js b/app/static/js/RessourceLists/TesseractOCRPipelineModelList.js new file mode 100644 index 00000000..36dea105 --- /dev/null +++ b/app/static/js/RessourceLists/TesseractOCRPipelineModelList.js @@ -0,0 +1,99 @@ +class TesseractOCRPipelineModelList extends RessourceList { + static autoInit() { + for (let tesseractOCRPipelineModelListElement of document.querySelectorAll('.tesseract-ocr-pipeline-model-list:not(.no-autoinit)')) { + new TesseractOCRPipelineModelList(tesseractOCRPipelineModelListElement); + } + } + + static options = { + initialHtmlGenerator: (id) => { + return ` + <div class="input-field"> + <i class="material-icons prefix">search</i> + <input id="${id}-search" class="search" type="search"></input> + <label for="${id}-search">Search Tesseract OCR Pipeline Model</label> + </div> + <table> + <thead> + <tr> + <th>Title</th> + <th>Description</th> + <th>Biblio</th> + <th></th> + </tr> + </thead> + <tbody class="list"></tbody> + </table> + <ul class="pagination"></ul> + `.trim(); + }, + item: ` + <tr class="clickable hoverable"> + <td><span class="title"></span></td> + <td><span class="description"></span></td> + <td><a class="publisher-url"><span class="publisher"></span></a> (<span class="publishing-year"></span>), <span class="title-2"></span> <span class="version"></span>, <a class="publishing-url"><span class="publishing-url-2"></span></a></td> + <td class="right-align"> + <a class="action-button btn-floating red waves-effect waves-light" data-action="delete-request"><i class="material-icons">delete</i></a> + <a class="action-button btn-floating service-color darken waves-effect waves-light service-2" data-action="view"><i class="material-icons">send</i></a> + </td> + </tr> + `.trim(), + ressourceMapper: (tesseractOCRPipelineModel) => { + return { + 'id': tesseractOCRPipelineModel.id, + 'creation-date': tesseractOCRPipelineModel.creation_date, + 'description': tesseractOCRPipelineModel.description, + 'publisher': tesseractOCRPipelineModel.publisher, + 'publisher-url': tesseractOCRPipelineModel.publisher_url, + 'publishing-url': tesseractOCRPipelineModel.publishing_url, + 'publishing-url-2': tesseractOCRPipelineModel.publishing_url, + 'publishing-year': tesseractOCRPipelineModel.publishing_year, + 'title': tesseractOCRPipelineModel.title, + 'title-2': tesseractOCRPipelineModel.title, + 'version': tesseractOCRPipelineModel.version + }; + }, + sortArgs: ['creation-date', {order: 'desc'}], + valueNames: [ + {data: ['id']}, + {data: ['creation-date']}, + {name: 'publisher-url', attr: 'href'}, + {name: 'publishing-url', attr: 'href'}, + 'description', + 'publisher', + 'publishing-url-2', + 'publishing-year', + 'title', + 'title-2', + 'version' + ] + }; + + constructor(listElement, options = {}) { + super(listElement, {...TesseractOCRPipelineModelList.options, ...options}); + } + + init (user) { + this._init(user.tesseract_ocr_pipeline_models); + } + + onClick(event) { + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; + let tesseractOCRPipelineModelElement = event.target.closest('tr'); + let tesseractOCRPipelineModelId = tesseractOCRPipelineModelElement.dataset.id; + switch (action) { + case 'delete-request': { + Utils.deleteTesseractOCRPipelineModelRequest(this.userId, tesseractOCRPipelineModelId); + break; + } + case 'view': { + window.location.href = `/contributions/tesseract-ocr-pipeline-models/${tesseractOCRPipelineModelId}`; + break; + } + default: { + break; + } + } + } +} diff --git a/app/static/js/Utils.js b/app/static/js/Utils.js index 2e7cbd4c..e6076e43 100644 --- a/app/static/js/Utils.js +++ b/app/static/js/Utils.js @@ -84,8 +84,8 @@ class Utils { ` <div class="modal"> <div class="modal-content"> - <h4>Confirm job deletion</h4> - <p>Do you really want to delete the job <b>${corpusFile.title}</b>? All files will be permanently deleted!</p> + <h4>Confirm Corpus File deletion</h4> + <p>Do you really want to delete the Corpus File <b>${corpusFile.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> @@ -126,6 +126,102 @@ class Utils { }); } + static deleteSpaCyNLPPipelineModelRequest(userId, spaCyNLPPipelineModelId) { + return new Promise((resolve, reject) => { + let spaCyNLPPipelineModel = app.data.users[userId].spacy_nlp_pipeline_models[spaCyNLPPipelineModelId]; + let modalElement = Utils.elementFromString( + ` + <div class="modal"> + <div class="modal-content"> + <h4>Confirm SpaCy NLP Pipeline Model deletion</h4> + <p>Do you really want to delete the SpaCy NLP Pipeline Model <b>${spaCyNLPPipelineModel.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 spaCyNLPPipelineModelTitle = spaCyNLPPipelineModel.title; + fetch(`/contributions/spacy-nlp-pipeline-models/${spaCyNLPPipelineModelId}`, {method: 'DELETE'}) + .then( + (response) => { + app.flash(`SpaCy NLP Pipeline Model "${spaCyNLPPipelineModelTitle}" marked for deletion`); + resolve(response); + }, + (response) => { + if (response.status === 403) {app.flash('Forbidden', 'error');} + if (response.status === 404) {app.flash('Not Found', 'error');} + reject(response); + } + ); + }); + modal.open(); + }); + } + + static deleteTesseractOCRPipelineModelRequest(userId, tesseractOCRPipelineModelId) { + return new Promise((resolve, reject) => { + let tesseractOCRPipelineModel = app.data.users[userId].tesseract_ocr_pipeline_models[tesseractOCRPipelineModelId]; + let modalElement = Utils.elementFromString( + ` + <div class="modal"> + <div class="modal-content"> + <h4>Confirm Tesseract OCR Pipeline Model deletion</h4> + <p>Do you really want to delete the Tesseract OCR Pipeline Model <b>${tesseractOCRPipelineModel.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 tesseractOCRPipelineModelTitle = tesseractOCRPipelineModel.title; + fetch(`/contributions/tesseract-ocr-pipeline-models/${tesseractOCRPipelineModelId}`, {method: 'DELETE'}) + .then( + (response) => { + app.flash(`Tesseract OCR Pipeline Model "${tesseractOCRPipelineModelTitle}" marked for deletion`); + resolve(response); + }, + (response) => { + if (response.status === 403) {app.flash('Forbidden', 'error');} + if (response.status === 404) {app.flash('Not Found', 'error');} + reject(response); + } + ); + }); + modal.open(); + }); + } + static deleteJobRequest(userId, jobId) { return new Promise((resolve, reject) => { let job = app.data.users[userId].jobs[jobId]; diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index 798d2848..49a4d69f 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -24,9 +24,9 @@ 'js/RessourceLists/JobList.js', 'js/RessourceLists/JobInputList.js', 'js/RessourceLists/JobResultList.js', + 'js/RessourceLists/SpacyNLPPipelineModelList.js', + 'js/RessourceLists/TesseractOCRPipelineModelList.js', 'js/RessourceLists/QueryResultList.js', - 'js/RessourceLists/SpacyNLPModelList.js', - 'js/RessourceLists/TesseractOCRModelList.js', 'js/RessourceLists/UserList.js' %} <script src="{{ ASSET_URL }}"></script> diff --git a/app/templates/contributions/_breadcrumbs.html.j2 b/app/templates/contributions/_breadcrumbs.html.j2 index 327d0578..21f27789 100644 --- a/app/templates/contributions/_breadcrumbs.html.j2 +++ b/app/templates/contributions/_breadcrumbs.html.j2 @@ -3,14 +3,14 @@ {% if request.path == url_for('.contributions') %} <li class="tab"><a class="active" href="{{ url_for('.contributions') }}" target="_self">Contributions Overview</a></li> -{% elif request.path == url_for('.add_tesseract_ocr_pipeline_model') %} +{% elif request.path == url_for('.create_tesseract_ocr_pipeline_model') %} <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('.add_tesseract_ocr_pipeline_model') }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.add_spacy_nlp_pipeline_model') %} +<li class="tab"><a class="active" href="{{ url_for('.create_tesseract_ocr_pipeline_model') }}" target="_self">{{ title }}</a></li> +{% elif request.path == url_for('.create_spacy_nlp_pipeline_model') %} <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('.add_spacy_nlp_pipeline_model') }}" target="_self">{{ title }}</a></li> +<li class="tab"><a class="active" href="{{ url_for('.create_spacy_nlp_pipeline_model') }}" target="_self">{{ title }}</a></li> {% elif tesseract_ocr_pipeline_model and request.path == url_for('.tesseract_ocr_pipeline_model', tesseract_ocr_pipeline_model_id=tesseract_ocr_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> diff --git a/app/templates/contributions/contribution_overview.html.j2 b/app/templates/contributions/contribution_overview.html.j2 deleted file mode 100644 index c6facf9d..00000000 --- a/app/templates/contributions/contribution_overview.html.j2 +++ /dev/null @@ -1,129 +0,0 @@ -{% extends "base.html.j2" %} -{% import "materialize/wtf.html.j2" as wtf %} -{% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %} - -{% block page_content %} -<div class="container"> - <div class="row"> - <div class="col s12"> - <h1 id="title">{{ title }}</h1> - - {# Tesseract OCR Models #} - <div> - <h3>My Tesseract OCR 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="tesseract-ocr-model-list" data-user-id="{{ userId }}"> - <table> - <thead> - <tr> - <th>Title</th> - <th>Description</th> - <th>Biblio</th> - <th></th> - </tr> - </thead> - <tbody> - {% if tesseract_ocr_user_models|length > 0 %} - {% for m in tesseract_ocr_user_models %} - <tr id="tesseract-ocr-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-button btn-floating red waves-effect waves-light" data-model-id="{{ m.hashid }}"><i class="material-icons">delete</i></a> - <a class="edit-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_tesseract_ocr_pipeline_model') }}" class="btn waves-effect waves-light"><i class="material-icons left">add</i>Add model file</a> - </div> - </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> -{% endblock page_content %} - -{% block scripts %} -{{ super() }} -<script> -const tesseractOCRModelList = new TesseractOCRModelList(); -tesseractOCRModelList.init(); -const spacyNLPModelList = new SpacyNLPModelList(); -spacyNLPModelList.init(); -</script> -{% endblock scripts %} diff --git a/app/templates/contributions/contributions.html.j2 b/app/templates/contributions/contributions.html.j2 new file mode 100644 index 00000000..da2cdcad --- /dev/null +++ b/app/templates/contributions/contributions.html.j2 @@ -0,0 +1,51 @@ +{% extends "base.html.j2" %} +{% import "materialize/wtf.html.j2" as wtf %} +{% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %} + +{% block page_content %} +<div class="container"> + <div class="row"> + <div class="col s12"> + <h1 id="title">{{ title }}</h1> + + {# Tesseract OCR Models #} + <div> + <h3>My Tesseract OCR 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 class="tesseract-ocr-pipeline-model-list" data-user-id="{{ current_user.hashid }}"></div> + </div> + <div class="card-action right-align"> + <a href="{{ url_for('.create_tesseract_ocr_pipeline_model') }}" class="btn waves-effect waves-light"><i class="material-icons left">add</i>Create</a> + </div> + </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 class="spacy-nlp-pipeline-model-list" data-user-id="{{ current_user.hashid }}"></div> + </div> + <div class="card-action right-align"> + <a href="{{ url_for('.create_spacy_nlp_pipeline_model') }}" class="btn waves-effect waves-light"><i class="material-icons left">add</i>Create</a> + </div> + </div> + </div> + </div> + </div> + </div> + </div> +</div> +{% endblock page_content %} diff --git a/app/templates/contributions/contribute_spacy_nlp_models.html.j2 b/app/templates/contributions/create_spacy_nlp_pipeline_model.html.j2 similarity index 70% rename from app/templates/contributions/contribute_spacy_nlp_models.html.j2 rename to app/templates/contributions/create_spacy_nlp_pipeline_model.html.j2 index d1e3cd11..9a0bcddd 100644 --- a/app/templates/contributions/contribute_spacy_nlp_models.html.j2 +++ b/app/templates/contributions/create_spacy_nlp_pipeline_model.html.j2 @@ -27,10 +27,8 @@ <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 spaCy NLP 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> + <span class="card-title"><i class="left material-icons">layers</i>SpaCy NLP Pipeline Model</span> + <p>You can create a new SpaCy NLP Pipeline Model using the form below. They will automatically appear in the list of usable models on the <a href="{{ url_for('services.spacy_nlp_pipeline') }}">SpaCy NLP Pipeline</a> service page.</p> </div> </div> </div> @@ -39,7 +37,6 @@ </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"> @@ -91,37 +88,3 @@ </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 %} diff --git a/app/templates/contributions/contribute_tesseract_ocr_models.html.j2 b/app/templates/contributions/create_tesseract_ocr_pipeline_model.html.j2 similarity index 91% rename from app/templates/contributions/contribute_tesseract_ocr_models.html.j2 rename to app/templates/contributions/create_tesseract_ocr_pipeline_model.html.j2 index 1e50585a..9a04b535 100644 --- a/app/templates/contributions/contribute_tesseract_ocr_models.html.j2 +++ b/app/templates/contributions/create_tesseract_ocr_pipeline_model.html.j2 @@ -27,10 +27,8 @@ <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>Tesseract OCR 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 class="modal-trigger" href="#models-modal">Information about the already existing models.</a></p> - <p><a href="">Edit already uploaded models</a></p> + <span class="card-title"><i class="left material-icons">layers</i>Tesseract OCR Pipeline Model</span> + <p>You can create a new Tesseract OCR Pipeline Model using the form below. They will automatically appear in the list of usable models on the <a href="{{ url_for('services.tesseract_ocr_pipeline') }}">Tesseract OCR Pipeline</a> service page.</p> </div> </div> </div> @@ -39,7 +37,6 @@ </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"> diff --git a/app/templates/contributions/spacy_nlp_pipeline_model.html.j2 b/app/templates/contributions/spacy_nlp_pipeline_model.html.j2 index 37248448..951b12a6 100644 --- a/app/templates/contributions/spacy_nlp_pipeline_model.html.j2 +++ b/app/templates/contributions/spacy_nlp_pipeline_model.html.j2 @@ -13,7 +13,7 @@ <div class="col s12"> <div class="card"> - <form class="create-contribution-form" enctype="multipart/form-data" method="POST"> + <form method="POST"> <div class="card-content"> {{ form.hidden_tag() }} <div class="row"> diff --git a/app/templates/contributions/tesseract_ocr_pipeline_model.html.j2 b/app/templates/contributions/tesseract_ocr_pipeline_model.html.j2 index 4db82349..c979f3ab 100644 --- a/app/templates/contributions/tesseract_ocr_pipeline_model.html.j2 +++ b/app/templates/contributions/tesseract_ocr_pipeline_model.html.j2 @@ -13,7 +13,7 @@ <div class="col s12"> <div class="card"> - <form class="create-contribution-form" enctype="multipart/form-data" method="POST"> + <form method="POST"> <div class="card-content"> {{ form.hidden_tag() }} <div class="row"> -- GitLab