From 90ac30bba3ce7c751244aa308bb8d5b843818250 Mon Sep 17 00:00:00 2001 From: Patrick Jentsch <p.jentsch@uni-bielefeld.de> Date: Mon, 13 Mar 2023 16:22:42 +0100 Subject: [PATCH] Implement Flask-Breadcrumbs --- app/__init__.py | 7 + app/auth/routes.py | 15 +- app/contributions/__init__.py | 14 +- app/contributions/forms.py | 81 +++++++- app/contributions/json_routes.py | 107 ++++++++++ app/contributions/routes.py | 183 +++++++++++++++++- .../spacy_nlp_pipeline_models/__init__.py | 8 - .../spacy_nlp_pipeline_models/forms.py | 44 ----- .../spacy_nlp_pipeline_models/json_routes.py | 54 ------ .../spacy_nlp_pipeline_models/routes.py | 76 -------- .../tesseract_ocr_pipeline_models/__init__.py | 8 - .../tesseract_ocr_pipeline_models/forms.py | 35 ---- .../json_routes.py | 54 ------ .../tesseract_ocr_pipeline_models/routes.py | 75 ------- app/corpora/routes.py | 21 +- app/main/routes.py | 9 + app/services/routes.py | 17 +- app/templates/_navbar.html.j2 | 8 +- app/templates/_sidenav.html.j2 | 2 +- app/templates/admin/_breadcrumbs.html.j2 | 18 -- app/templates/admin/edit_user.html.j2 | 1 - app/templates/admin/user.html.j2 | 1 - app/templates/admin/users.html.j2 | 1 - app/templates/auth/_breadcrumbs.html.j2 | 14 -- app/templates/auth/login.html.j2 | 1 - app/templates/auth/register.html.j2 | 1 - app/templates/auth/reset_password.html.j2 | 1 - .../auth/reset_password_request.html.j2 | 1 - app/templates/auth/unconfirmed.html.j2 | 1 - .../contributions/_breadcrumbs.html.j2 | 46 ----- .../contributions/contributions.html.j2 | 4 +- .../create_spacy_nlp_pipeline_model.html.j2 | 0 ...reate_tesseract_ocr_pipeline_model.html.j2 | 0 .../spacy_nlp_pipeline_model.html.j2 | 0 .../spacy_nlp_pipeline_models.html.j2 | 0 .../tesseract_ocr_pipeline_model.html.j2 | 0 .../tesseract_ocr_pipeline_models.html.j2 | 0 app/templates/corpora/_breadcrumbs.html.j2 | 28 --- app/templates/corpora/corpus.html.j2 | 1 - app/templates/corpora/create_corpus.html.j2 | 1 - .../corpora/files/corpus_file.html.j2 | 1 - .../corpora/files/create_corpus_file.html.j2 | 1 - app/templates/corpora/import_corpus.html.j2 | 1 - app/templates/jobs/_breadcrumbs.html.j2 | 8 - app/templates/jobs/job.html.j2 | 1 - app/templates/main/_breadcrumbs.html.j2 | 14 -- app/templates/main/dashboard.html.j2 | 1 - app/templates/main/faq.html.j2 | 1 - app/templates/main/index.html.j2 | 1 - app/templates/main/news.html.j2 | 1 - app/templates/main/news_new.html.j2 | 1 - app/templates/main/privacy_policy.html.j2 | 1 - app/templates/main/terms_of_use.html.j2 | 1 - app/templates/main/user_manual.html.j2 | 1 - app/templates/services/_breadcrumbs.html.j2 | 16 -- .../services/corpus_analysis.html.j2 | 1 - .../services/file_setup_pipeline.html.j2 | 1 - .../services/spacy_nlp_pipeline.html.j2 | 3 +- .../services/tesseract_ocr_pipeline.html.j2 | 3 +- .../services/transkribus_htr_pipeline.html.j2 | 1 - app/templates/settings/_breadcrumbs.html.j2 | 6 - app/templates/settings/settings.html.j2 | 1 - requirements.txt | 1 + 63 files changed, 425 insertions(+), 580 deletions(-) create mode 100644 app/contributions/json_routes.py delete mode 100644 app/contributions/spacy_nlp_pipeline_models/__init__.py delete mode 100644 app/contributions/spacy_nlp_pipeline_models/forms.py delete mode 100644 app/contributions/spacy_nlp_pipeline_models/json_routes.py delete mode 100644 app/contributions/spacy_nlp_pipeline_models/routes.py delete mode 100644 app/contributions/tesseract_ocr_pipeline_models/__init__.py delete mode 100644 app/contributions/tesseract_ocr_pipeline_models/forms.py delete mode 100644 app/contributions/tesseract_ocr_pipeline_models/json_routes.py delete mode 100644 app/contributions/tesseract_ocr_pipeline_models/routes.py delete mode 100644 app/templates/admin/_breadcrumbs.html.j2 delete mode 100644 app/templates/auth/_breadcrumbs.html.j2 delete mode 100644 app/templates/contributions/_breadcrumbs.html.j2 rename app/templates/contributions/{spacy_nlp_pipeline_models => }/create_spacy_nlp_pipeline_model.html.j2 (100%) rename app/templates/contributions/{tesseract_ocr_pipeline_models => }/create_tesseract_ocr_pipeline_model.html.j2 (100%) rename app/templates/contributions/{spacy_nlp_pipeline_models => }/spacy_nlp_pipeline_model.html.j2 (100%) rename app/templates/contributions/{spacy_nlp_pipeline_models => }/spacy_nlp_pipeline_models.html.j2 (100%) rename app/templates/contributions/{tesseract_ocr_pipeline_models => }/tesseract_ocr_pipeline_model.html.j2 (100%) rename app/templates/contributions/{tesseract_ocr_pipeline_models => }/tesseract_ocr_pipeline_models.html.j2 (100%) delete mode 100644 app/templates/corpora/_breadcrumbs.html.j2 delete mode 100644 app/templates/jobs/_breadcrumbs.html.j2 delete mode 100644 app/templates/main/_breadcrumbs.html.j2 delete mode 100644 app/templates/services/_breadcrumbs.html.j2 delete mode 100644 app/templates/settings/_breadcrumbs.html.j2 diff --git a/app/__init__.py b/app/__init__.py index dcb58e47..59d6d5c9 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -4,6 +4,7 @@ from docker import DockerClient from flask import Flask from flask_apscheduler import APScheduler from flask_assets import Environment +from flask_breadcrumbs import Breadcrumbs, default_breadcrumb_root from flask_login import LoginManager from flask_mail import Mail from flask_marshmallow import Marshmallow @@ -16,6 +17,7 @@ from flask_hashids import Hashids apifairy = APIFairy() assets = Environment() +breadcrumbs = Breadcrumbs() db = SQLAlchemy() docker_client = DockerClient() hashids = Hashids() @@ -44,6 +46,7 @@ def create_app(config: Config = Config) -> Flask: apifairy.init_app(app) assets.init_app(app) + breadcrumbs.init_app(app) db.init_app(app) hashids.init_app(app) login.init_app(app) @@ -64,9 +67,11 @@ def create_app(config: Config = Config) -> Flask: app.register_blueprint(api_blueprint, url_prefix='/api') from .auth import bp as auth_blueprint + default_breadcrumb_root(auth_blueprint, '.') app.register_blueprint(auth_blueprint, url_prefix='/auth') from .contributions import bp as contributions_blueprint + default_breadcrumb_root(contributions_blueprint, '.contributions') app.register_blueprint(contributions_blueprint, url_prefix='/contributions') from .corpora import bp as corpora_blueprint @@ -76,9 +81,11 @@ def create_app(config: Config = Config) -> Flask: app.register_blueprint(jobs_blueprint, url_prefix='/jobs') from .main import bp as main_blueprint + default_breadcrumb_root(main_blueprint, '.') app.register_blueprint(main_blueprint, url_prefix='/') from .services import bp as services_blueprint + default_breadcrumb_root(services_blueprint, '.services') app.register_blueprint(services_blueprint, url_prefix='/services') from .settings import bp as settings_blueprint diff --git a/app/auth/routes.py b/app/auth/routes.py index 5655d0dc..6e11a140 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -1,11 +1,5 @@ -from flask import ( - abort, - flash, - redirect, - render_template, - request, - url_for -) +from flask import abort, flash, redirect, render_template, request, url_for +from flask_breadcrumbs import register_breadcrumb from flask_login import current_user, login_user, login_required, logout_user from app import db from app.email import create_message, send @@ -36,6 +30,7 @@ def before_request(): @bp.route('/register', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.register', 'Register') def register(): if current_user.is_authenticated: return redirect(url_for('main.dashboard')) @@ -71,6 +66,7 @@ def register(): @bp.route('/login', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.login', 'Login') def login(): if current_user.is_authenticated: return redirect(url_for('main.dashboard')) @@ -97,6 +93,7 @@ def logout(): @bp.route('/unconfirmed') +@register_breadcrumb(bp, '.unconfirmed', 'Unconfirmed') @login_required def unconfirmed(): if current_user.confirmed: @@ -136,6 +133,7 @@ def confirm(token): @bp.route('/reset_password', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.reset_password_request', 'Password Reset') def reset_password_request(): if current_user.is_authenticated: return redirect(url_for('main.dashboard')) @@ -165,6 +163,7 @@ def reset_password_request(): @bp.route('/reset_password/<token>', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.reset_password', 'Password Reset') def reset_password(token): if current_user.is_authenticated: return redirect(url_for('main.dashboard')) diff --git a/app/contributions/__init__.py b/app/contributions/__init__.py index 5175c0ce..7749a278 100644 --- a/app/contributions/__init__.py +++ b/app/contributions/__init__.py @@ -2,16 +2,4 @@ from flask import Blueprint bp = Blueprint('contributions', __name__) -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' -) +from . import json_routes, routes diff --git a/app/contributions/forms.py b/app/contributions/forms.py index acec307f..1ef4fdc7 100644 --- a/app/contributions/forms.py +++ b/app/contributions/forms.py @@ -1,11 +1,14 @@ from flask_wtf import FlaskForm +from flask_wtf.file import FileField, FileRequired from wtforms import ( StringField, SubmitField, SelectMultipleField, - IntegerField + IntegerField, + ValidationError ) from wtforms.validators import InputRequired, Length +from app.services import SERVICES class ContributionBaseForm(FlaskForm): @@ -45,3 +48,79 @@ class ContributionBaseForm(FlaskForm): class EditContributionBaseForm(ContributionBaseForm): pass + + +############################################################################## +# /spacy-nlp-pipeline-models # +############################################################################## +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 = '' + + +############################################################################## +# /tesseract-ocr-pipeline-models # +############################################################################## +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 = '' diff --git a/app/contributions/json_routes.py b/app/contributions/json_routes.py new file mode 100644 index 00000000..c44a4c9c --- /dev/null +++ b/app/contributions/json_routes.py @@ -0,0 +1,107 @@ +from flask import abort, current_app, 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, TesseractOCRPipelineModel +from . import bp + + +############################################################################## +# /spacy-nlp-pipeline-models # +############################################################################## +@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_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' + } + return resonse_data, 202 + + +@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): + 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"}' + ) + } + return response_data, 200 + + +############################################################################## +# /tesseract-ocr-pipeline-models # +############################################################################## +@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_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() + response_data = { + 'message': \ + f'Tesseract OCR Pipeline Model "{topm.title}" marked for deletion' + } + return response_data, 202 + + +@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): + 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"}' + ) + } + return response_data, 200 diff --git a/app/contributions/routes.py b/app/contributions/routes.py index 6d8b9cc3..6da7dc12 100644 --- a/app/contributions/routes.py +++ b/app/contributions/routes.py @@ -1,12 +1,189 @@ -from flask import render_template -from flask_login import login_required +from flask import abort, flash, redirect, render_template, request, url_for +from flask_breadcrumbs import register_breadcrumb +from flask_login import current_user, login_required +from app import db +from app.models import SpaCyNLPPipelineModel, TesseractOCRPipelineModel from . import bp +from .forms import ( + CreateSpaCyNLPPipelineModelForm, + EditSpaCyNLPPipelineModelForm, + CreateTesseractOCRPipelineModelForm, + EditTesseractOCRPipelineModelForm +) -@bp.route('/') +@bp.route('') +@register_breadcrumb(bp, '.', 'Contributions') @login_required def contributions(): return render_template( 'contributions/contributions.html.j2', title='Contributions' ) + + +############################################################################## +# /spacy-nlp-pipeline-models # +############################################################################## +@bp.route('/spacy-nlp-pipeline-models') +@register_breadcrumb(bp, '.spacy_nlp_pipeline_models', 'SpaCy NLP Pipeline Models') +@login_required +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']) +@register_breadcrumb(bp, '.spacy_nlp_pipeline_models.create', 'Create') +@login_required +def create_spacy_nlp_pipeline_model(): + form_prefix = 'create-spacy-nlp-pipeline-model-form' + form = CreateSpaCyNLPPipelineModelForm(prefix=form_prefix) + if form.is_submitted(): + if not form.validate(): + return {'errors': form.errors}, 400 + try: + snpm = 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() + flash(f'SpaCy NLP Pipeline model "{snpm.title}" created') + return {}, 201, {'Location': url_for('.spacy_nlp_pipeline_models')} + return render_template( + 'contributions/create_spacy_nlp_pipeline_model.html.j2', + form=form, + title='Create SpaCy NLP Pipeline Model' + ) + + +def spacy_nlp_pipeline_model_dlc(*args, **kwargs): + snpm_id = request.view_args['spacy_nlp_pipeline_model_id'] + snpm = SpaCyNLPPipelineModel.query.get(snpm_id) + return [ + { + 'text': f'{snpm.title} {snpm.version}', + 'url': url_for('.spacy_nlp_pipeline_model', spacy_nlp_pipeline_model_id=snpm_id) + } + ] + + +@bp.route('/spacy-nlp-pipeline-models/<hashid:spacy_nlp_pipeline_model_id>', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.spacy_nlp_pipeline_models.entity', '', dynamic_list_constructor=spacy_nlp_pipeline_model_dlc) +@login_required +def spacy_nlp_pipeline_model(spacy_nlp_pipeline_model_id): + snpm = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id) + form_prefix = 'edit-spacy-nlp-pipeline-model-form' + form = EditSpaCyNLPPipelineModelForm( + data=snpm.to_json_serializeable(), + prefix=form_prefix + ) + if form.validate_on_submit(): + form.populate_obj(snpm) + if db.session.is_modified(snpm): + flash(f'SpaCy NLP Pipeline model "{snpm.title}" updated') + 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=snpm, + title=f'{snpm.title} {snpm.version}' + ) + + +############################################################################## +# /tesseract-ocr-pipeline-models # +############################################################################## +@bp.route('/tesseract-ocr-pipeline-models') +@register_breadcrumb(bp, '.tesseract_ocr_pipeline_models', 'Tesseract OCR Pipeline Models') +@login_required +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']) +@register_breadcrumb(bp, '.tesseract_ocr_pipeline_models.create', 'Create') +@login_required +def create_tesseract_ocr_pipeline_model(): + form_prefix = 'create-tesseract-ocr-pipeline-model-form' + form = CreateTesseractOCRPipelineModelForm(prefix=form_prefix) + if form.is_submitted(): + if not form.validate(): + return {'errors': form.errors}, 400 + try: + topm = 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() + flash(f'Tesseract OCR Pipeline model "{topm.title}" created') + return {}, 201, {'Location': url_for('.tesseract_ocr_pipeline_models')} + return render_template( + 'contributions/create_tesseract_ocr_pipeline_model.html.j2', + form=form, + title='Create Tesseract OCR Pipeline Model' + ) + + +def tesseract_ocr_pipeline_model_dlc(*args, **kwargs): + topm_id = request.view_args['tesseract_ocr_pipeline_model_id'] + topm = TesseractOCRPipelineModel.query.get(topm_id) + return [ + { + 'text': f'{topm.title} {topm.version}', + 'url': url_for('.tesseract_ocr_pipeline_model', tesseract_ocr_pipeline_model_id=topm_id) + } + ] + + +@bp.route('/tesseract-ocr-pipeline-models/<hashid:tesseract_ocr_pipeline_model_id>', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.tesseract_ocr_pipeline_models.entity', '', dynamic_list_constructor=tesseract_ocr_pipeline_model_dlc) +@login_required +def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id): + topm = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id) + form_prefix = 'edit-tesseract-ocr-pipeline-model-form' + form = EditTesseractOCRPipelineModelForm( + data=topm.to_json_serializeable(), + prefix=form_prefix + ) + if form.validate_on_submit(): + form.populate_obj(topm) + if db.session.is_modified(topm): + flash(f'Tesseract OCR Pipeline model "{topm.title}" updated') + 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=topm, + title=f'{topm.title} {topm.version}' + ) diff --git a/app/contributions/spacy_nlp_pipeline_models/__init__.py b/app/contributions/spacy_nlp_pipeline_models/__init__.py deleted file mode 100644 index 8ff119d0..00000000 --- a/app/contributions/spacy_nlp_pipeline_models/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from flask import Blueprint - - -template_base_dir = 'contributions/spacy_nlp_pipeline_models' - - -bp = Blueprint('spacy_nlp_pipeline_models', __name__) -from . import routes, json_routes diff --git a/app/contributions/spacy_nlp_pipeline_models/forms.py b/app/contributions/spacy_nlp_pipeline_models/forms.py deleted file mode 100644 index 2670c1d1..00000000 --- a/app/contributions/spacy_nlp_pipeline_models/forms.py +++ /dev/null @@ -1,44 +0,0 @@ -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 = '' diff --git a/app/contributions/spacy_nlp_pipeline_models/json_routes.py b/app/contributions/spacy_nlp_pipeline_models/json_routes.py deleted file mode 100644 index f7a9a254..00000000 --- a/app/contributions/spacy_nlp_pipeline_models/json_routes.py +++ /dev/null @@ -1,54 +0,0 @@ -from flask import abort, current_app, 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' - } - return resonse_data, 202 - - -@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"}' - ) - } - return response_data, 200 diff --git a/app/contributions/spacy_nlp_pipeline_models/routes.py b/app/contributions/spacy_nlp_pipeline_models/routes.py deleted file mode 100644 index fd972902..00000000 --- a/app/contributions/spacy_nlp_pipeline_models/routes.py +++ /dev/null @@ -1,76 +0,0 @@ -from flask import abort, flash, redirect, render_template, url_for -from flask_login import current_user, login_required -from app import db -from app.models import SpaCyNLPPipelineModel -from . import bp, template_base_dir -from .forms import ( - CreateSpaCyNLPPipelineModelForm, - EditSpaCyNLPPipelineModelForm -) - - -@bp.route('') -@login_required -def spacy_nlp_pipeline_models(): - return render_template( - f'{template_base_dir}/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_prefix = 'create-spacy-nlp-pipeline-model-form' - form = CreateSpaCyNLPPipelineModelForm(prefix=form_prefix) - if form.is_submitted(): - if not form.validate(): - return {'errors': form.errors}, 400 - try: - snpm = 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() - flash(f'SpaCy NLP Pipeline model "{snpm.title}" created') - return {}, 201, {'Location': url_for('.spacy_nlp_pipeline_models')} - return render_template( - f'{template_base_dir}/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): - snpm = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id) - form_prefix = 'edit-spacy-nlp-pipeline-model-form' - form = EditSpaCyNLPPipelineModelForm( - data=snpm.to_json_serializeable(), - prefix=form_prefix - ) - if form.validate_on_submit(): - form.populate_obj(snpm) - if db.session.is_modified(snpm): - flash(f'SpaCy NLP Pipeline model "{snpm.title}" updated') - db.session.commit() - return redirect(url_for('.spacy_nlp_pipeline_models')) - return render_template( - f'{template_base_dir}/spacy_nlp_pipeline_model.html.j2', - form=form, - spacy_nlp_pipeline_model=snpm, - title=f'{snpm.title} {snpm.version}' - ) diff --git a/app/contributions/tesseract_ocr_pipeline_models/__init__.py b/app/contributions/tesseract_ocr_pipeline_models/__init__.py deleted file mode 100644 index cf44126d..00000000 --- a/app/contributions/tesseract_ocr_pipeline_models/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from flask import Blueprint - - -template_base_dir = 'contributions/tesseract_ocr_pipeline_models' - - -bp = Blueprint('tesseract_ocr_pipeline_models', __name__) -from . import routes, json_routes diff --git a/app/contributions/tesseract_ocr_pipeline_models/forms.py b/app/contributions/tesseract_ocr_pipeline_models/forms.py deleted file mode 100644 index 51f0d76c..00000000 --- a/app/contributions/tesseract_ocr_pipeline_models/forms.py +++ /dev/null @@ -1,35 +0,0 @@ -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 = '' diff --git a/app/contributions/tesseract_ocr_pipeline_models/json_routes.py b/app/contributions/tesseract_ocr_pipeline_models/json_routes.py deleted file mode 100644 index 81aa6598..00000000 --- a/app/contributions/tesseract_ocr_pipeline_models/json_routes.py +++ /dev/null @@ -1,54 +0,0 @@ -from flask import abort, current_app, 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() - response_data = { - 'message': \ - f'Tesseract OCR Pipeline Model "{topm.title}" marked for deletion' - } - return response_data, 202 - - -@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"}' - ) - } - return response_data, 200 diff --git a/app/contributions/tesseract_ocr_pipeline_models/routes.py b/app/contributions/tesseract_ocr_pipeline_models/routes.py deleted file mode 100644 index b888cef7..00000000 --- a/app/contributions/tesseract_ocr_pipeline_models/routes.py +++ /dev/null @@ -1,75 +0,0 @@ -from flask import abort, flash, 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_base_dir -from .forms import ( - CreateTesseractOCRPipelineModelForm, - EditTesseractOCRPipelineModelForm -) - - -@bp.route('') -@login_required -def tesseract_ocr_pipeline_models(): - return render_template( - f'{template_base_dir}/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_prefix = 'create-tesseract-ocr-pipeline-model-form' - form = CreateTesseractOCRPipelineModelForm(prefix=form_prefix) - if form.is_submitted(): - if not form.validate(): - return {'errors': form.errors}, 400 - try: - topm = 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() - flash(f'Tesseract OCR Pipeline model "{topm.title}" created') - return {}, 201, {'Location': url_for('.tesseract_ocr_pipeline_models')} - return render_template( - f'{template_base_dir}/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): - topm = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id) - form_prefix = 'edit-tesseract-ocr-pipeline-model-form' - form = EditTesseractOCRPipelineModelForm( - data=topm.to_json_serializeable(), - prefix=form_prefix - ) - if form.validate_on_submit(): - form.populate_obj(topm) - if db.session.is_modified(topm): - flash(f'Tesseract OCR Pipeline model "{topm.title}" updated') - db.session.commit() - return redirect(url_for('.tesseract_ocr_pipeline_models')) - return render_template( - f'{template_base_dir}/tesseract_ocr_pipeline_model.html.j2', - form=form, - tesseract_ocr_pipeline_model=topm, - title=f'{topm.title} {topm.version}' - ) diff --git a/app/corpora/routes.py b/app/corpora/routes.py index 5484fb50..82701cea 100644 --- a/app/corpora/routes.py +++ b/app/corpora/routes.py @@ -1,23 +1,18 @@ -from flask import ( - abort, - flash, - Markup, - redirect, - render_template, - url_for -) +from flask import abort, flash, redirect, render_template, url_for from flask_login import current_user, login_required from .decorators import corpus_follower_permission_required from app import db -from app.models import ( - Corpus, - CorpusFollowerAssociation, - CorpusFollowerRole, -) +from app.models import Corpus, CorpusFollowerAssociation, CorpusFollowerRole from . import bp from .forms import CreateCorpusForm +@bp.route('') +@login_required +def corpora(): + return redirect(url_for('main.dashboard', _anchor='corpora')) + + @bp.route('/create', methods=['GET', 'POST']) @login_required def create_corpus(): diff --git a/app/main/routes.py b/app/main/routes.py index 918f7414..5399b5e9 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -1,4 +1,5 @@ from flask import flash, redirect, render_template, url_for +from flask_breadcrumbs import register_breadcrumb from flask_login import current_user, login_required, login_user from app.auth.forms import LoginForm from app.models import Corpus, User @@ -6,6 +7,7 @@ from . import bp @bp.route('', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.', '<i class="material-icons">home</i>') def index(): form = LoginForm(prefix='login-form') if form.validate_on_submit(): @@ -20,37 +22,44 @@ def index(): @bp.route('/faq') +@register_breadcrumb(bp, '.faq', 'Frequently Asked Questions') def faq(): return render_template('main/faq.html.j2', title='Frequently Asked Questions') @bp.route('/dashboard') +@register_breadcrumb(bp, '.dashboard', 'Dashboard') @login_required def dashboard(): return render_template('main/dashboard.html.j2', title='Dashboard') @bp.route('/user_manual') +@register_breadcrumb(bp, '.user_manual', 'User manual') def user_manual(): return render_template('main/user_manual.html.j2', title='User manual') @bp.route('/news') +@register_breadcrumb(bp, '.news', 'News') def news(): return render_template('main/news.html.j2', title='News') @bp.route('/privacy_policy') +@register_breadcrumb(bp, '.privacy_policy', 'Private statement (GDPR)') def privacy_policy(): return render_template('main/privacy_policy.html.j2', title='Privacy statement (GDPR)') @bp.route('/terms_of_use') +@register_breadcrumb(bp, '.terms_of_use', 'Terms of Use') def terms_of_use(): return render_template('main/terms_of_use.html.j2', title='Terms of Use') @bp.route('/social-area') +@register_breadcrumb(bp, '.social_area', 'Social Area') def social_area(): users = [ u.to_json_serializeable(relationships=True, filter_by_privacy_settings=True,) for u diff --git a/app/services/routes.py b/app/services/routes.py index 0fb4b168..5d5a69a0 100644 --- a/app/services/routes.py +++ b/app/services/routes.py @@ -1,4 +1,5 @@ -from flask import abort, current_app, flash, make_response, Markup, render_template, request +from flask import abort, current_app, flash, Markup, redirect, render_template, request, url_for +from flask_breadcrumbs import register_breadcrumb from flask_login import current_user, login_required import requests from app import db, hashids @@ -18,7 +19,15 @@ from .forms import ( ) +@bp.route('/services') +@register_breadcrumb(bp, '.', 'Services') +@login_required +def services(): + return redirect(url_for('main.dashboard')) + + @bp.route('/file-setup-pipeline', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.file_setup_pipeline', 'File Setup') @login_required def file_setup_pipeline(): service = 'file-setup-pipeline' @@ -60,6 +69,7 @@ def file_setup_pipeline(): @bp.route('/tesseract-ocr-pipeline', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.tesseract_ocr_pipeline', 'Tesseract OCR Pipeline') @login_required def tesseract_ocr_pipeline(): service_name = 'tesseract-ocr-pipeline' @@ -109,6 +119,7 @@ def tesseract_ocr_pipeline(): @bp.route('/transkribus-htr-pipeline', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.transkribus_htr_pipeline', 'Transkribus HTR Pipeline') @login_required def transkribus_htr_pipeline(): if not current_app.config.get('NOPAQUE_TRANSKRIBUS_ENABLED'): @@ -168,6 +179,7 @@ def transkribus_htr_pipeline(): @bp.route('/spacy-nlp-pipeline', methods=['GET', 'POST']) +@register_breadcrumb(bp, '.spacy_nlp_pipeline', 'SpaCy NLP Pipeline') @login_required def spacy_nlp_pipeline(): service = 'spacy-nlp-pipeline' @@ -213,9 +225,10 @@ def spacy_nlp_pipeline(): @bp.route('/corpus-analysis') +@register_breadcrumb(bp, '.corpus_analysis', 'Corpus Analysis') @login_required def corpus_analysis(): return render_template( 'services/corpus_analysis.html.j2', - title='Corpus analysis' + title='Corpus Analysis' ) diff --git a/app/templates/_navbar.html.j2 b/app/templates/_navbar.html.j2 index d943c0e4..2790ef1a 100644 --- a/app/templates/_navbar.html.j2 +++ b/app/templates/_navbar.html.j2 @@ -14,10 +14,12 @@ </div> <div class="nav-content primary-variant-color"> <ul class="tabs tabs-transparent"> - <li class="tab"><a href="{{ url_for('main.index') }}" target="_self"><i class="material-icons">home</i></a></li> - {% if breadcrumbs is defined %} - {{ breadcrumbs }} + {%- for breadcrumb in breadcrumbs -%} + <li class="tab"><a {{ 'class="active"' if loop.last }} href="{{ breadcrumb.url }}" target="_self">{{ breadcrumb.text }}</a></li> + {% if not loop.last %} + <li class="tab disabled"><i class="material-icons">navigate_next</i></li> {% endif %} + {%- endfor -%} </ul> {% if current_user.is_authenticated %} <a class="btn-floating btn-large halfway-fab modal-trigger pink tooltipped waves-effect waves-light" data-tooltip="Roadmap" href="#roadmap-modal"><i class="material-icons">explore</i></a> diff --git a/app/templates/_sidenav.html.j2 b/app/templates/_sidenav.html.j2 index 0027772e..fbaeddbe 100644 --- a/app/templates/_sidenav.html.j2 +++ b/app/templates/_sidenav.html.j2 @@ -35,7 +35,7 @@ <li class="service-color service-color-border border-darken" data-service="transkribus-htr-pipeline" style="border-left: 10px solid; margin-top: 5px;"><a href="{{ url_for('services.transkribus_htr_pipeline') }}"><i class="nopaque-icons service-icons" data-service="transkribus-htr-pipeline"></i>HTR</a></li> {% endif %} <li class="service-color service-color-border border-darken" data-service="spacy-nlp-pipeline" style="border-left: 10px solid; margin-top: 5px;"><a href="{{ url_for('services.spacy_nlp_pipeline') }}"><i class="nopaque-icons service-icons" data-service="spacy-nlp-pipeline"></i>NLP</a></li> - <li class="service-color service-color-border border-darken" data-service="corpus-analysis" style="border-left: 10px solid; margin-top: 5px;"><a href="{{ url_for('services.corpus_analysis') }}"><i class="nopaque-icons service-icons" data-service="corpus-analysis"></i>Corpus analysis</a></li> + <li class="service-color service-color-border border-darken" data-service="corpus-analysis" style="border-left: 10px solid; margin-top: 5px;"><a href="{{ url_for('services.corpus_analysis') }}"><i class="nopaque-icons service-icons" data-service="corpus-analysis"></i>Corpus Analysis</a></li> <li><div class="divider"></div></li> <li><a class="subheader">Account</a></li> <li><a href="{{ url_for('settings.settings') }}"><i class="material-icons">settings</i>General Settings</a></li> diff --git a/app/templates/admin/_breadcrumbs.html.j2 b/app/templates/admin/_breadcrumbs.html.j2 deleted file mode 100644 index c4a64046..00000000 --- a/app/templates/admin/_breadcrumbs.html.j2 +++ /dev/null @@ -1,18 +0,0 @@ -{% set breadcrumbs %} -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a href="{{ url_for('.index') }}" target="_self">Administration</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -{% if request.path == url_for('.users') %} -<li class="tab"><a class="active" href="{{ url_for('.users') }}" target="_self">Users</a></li> -{% elif request.path == url_for('.user', user_id=user.id) %} -<li class="tab"><a href="{{ url_for('.users') }}" target="_self">Users</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a class="active" href="{{ url_for('.user', user_id=user.id) }}" target="_self">{{ user.username }}</a></li> -{% elif request.path == url_for('.edit_user', user_id=user.id) %} -<li class="tab"><a href="{{ url_for('.users') }}" target="_self">Users</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a href="{{ url_for('.user', user_id=user.id) }}" target="_self">{{ user.username }}</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a class="active" href="{{ url_for('.edit_user', user_id=user.id) }}" target="_self">Edit</a></li> -{% endif %} -{% endset %} diff --git a/app/templates/admin/edit_user.html.j2 b/app/templates/admin/edit_user.html.j2 index c963b7f7..0ac4f27d 100644 --- a/app/templates/admin/edit_user.html.j2 +++ b/app/templates/admin/edit_user.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "admin/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block page_content %} diff --git a/app/templates/admin/user.html.j2 b/app/templates/admin/user.html.j2 index b4b0e303..aa4899ce 100644 --- a/app/templates/admin/user.html.j2 +++ b/app/templates/admin/user.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "admin/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block page_content %} <div class="container"> diff --git a/app/templates/admin/users.html.j2 b/app/templates/admin/users.html.j2 index c96024cf..25b27c95 100644 --- a/app/templates/admin/users.html.j2 +++ b/app/templates/admin/users.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "admin/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block page_content %} <div class="container"> diff --git a/app/templates/auth/_breadcrumbs.html.j2 b/app/templates/auth/_breadcrumbs.html.j2 deleted file mode 100644 index 2f46d9dc..00000000 --- a/app/templates/auth/_breadcrumbs.html.j2 +++ /dev/null @@ -1,14 +0,0 @@ -{% set breadcrumbs %} -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -{% if request.path == url_for('.login') %} -<li class="tab"><a class="active" href="{{ url_for('.login') }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.register') %} -<li class="tab"><a class="active" href="{{ url_for('.register') }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.reset_password', token=token) %} -<li class="tab"><a class="active" href="{{ url_for('.reset_password', token=token) }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.reset_password_request') %} -<li class="tab"><a class="active" href="{{ url_for('.reset_password_request') }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.unconfirmed') %} -<li class="tab"><a class="active" href="{{ url_for('.unconfirmed') }}" target="_self">{{ title }}</a></li> -{% endif %} -{% endset %} diff --git a/app/templates/auth/login.html.j2 b/app/templates/auth/login.html.j2 index 213c0a5f..7aa402f6 100644 --- a/app/templates/auth/login.html.j2 +++ b/app/templates/auth/login.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "auth/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} diff --git a/app/templates/auth/register.html.j2 b/app/templates/auth/register.html.j2 index 69a01912..d8e023a7 100644 --- a/app/templates/auth/register.html.j2 +++ b/app/templates/auth/register.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "auth/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} diff --git a/app/templates/auth/reset_password.html.j2 b/app/templates/auth/reset_password.html.j2 index 06f11059..7df460bb 100644 --- a/app/templates/auth/reset_password.html.j2 +++ b/app/templates/auth/reset_password.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "auth/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block page_content %} diff --git a/app/templates/auth/reset_password_request.html.j2 b/app/templates/auth/reset_password_request.html.j2 index a94d18da..5bf28111 100644 --- a/app/templates/auth/reset_password_request.html.j2 +++ b/app/templates/auth/reset_password_request.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "auth/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block page_content %} diff --git a/app/templates/auth/unconfirmed.html.j2 b/app/templates/auth/unconfirmed.html.j2 index 26384ecc..f927fcff 100644 --- a/app/templates/auth/unconfirmed.html.j2 +++ b/app/templates/auth/unconfirmed.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "auth/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block page_content %} <div class="container"> diff --git a/app/templates/contributions/_breadcrumbs.html.j2 b/app/templates/contributions/_breadcrumbs.html.j2 deleted file mode 100644 index ab64dec4..00000000 --- a/app/templates/contributions/_breadcrumbs.html.j2 +++ /dev/null @@ -1,46 +0,0 @@ -{% set breadcrumbs %} -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -{% 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('.tesseract_ocr_pipeline_models')%} -<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('.tesseract_ocr_pipeline_models') }}" target="_self">Tesseract OCR Pipeline Models</a></li> -{% elif request.path == url_for('.spacy_nlp_pipeline_models')%} -<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_models') }}" target="_self">SpaCy NLP Pipeline Models</a></li> -{% 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 href="{{ url_for('.tesseract_ocr_pipeline_models') }}" target="_self">Tesseract OCR Pipeline Models</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<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 href="{{ url_for('.spacy_nlp_pipeline_models') }}" target="_self">SpaCy NLP Pipeline Models</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></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> -<li class="tab"><a href="{{ url_for('.tesseract_ocr_pipeline_models') }}" target="_self">Tesseract OCR Pipeline Models</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"> - <a class="active" href="{{ url_for('.tesseract_ocr_pipeline_model', tesseract_ocr_pipeline_model_id=tesseract_ocr_pipeline_model.id) }}" target="_self"> - {{ tesseract_ocr_pipeline_model.title }} {{ tesseract_ocr_pipeline_model.version }} - </a> -</li> -{% elif spacy_nlp_pipeline_model and 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 href="{{ url_for('.spacy_nlp_pipeline_models') }}" target="_self">SpaCy NLP Pipeline Models</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.id) }}" target="_self"> - {{ spacy_nlp_pipeline_model.title }} {{ spacy_nlp_pipeline_model.version }} - </a> -</li> -{% endif %} -{% endset %} diff --git a/app/templates/contributions/contributions.html.j2 b/app/templates/contributions/contributions.html.j2 index efefcbbb..a0944723 100644 --- a/app/templates/contributions/contributions.html.j2 +++ b/app/templates/contributions/contributions.html.j2 @@ -10,7 +10,7 @@ <div class="col s4"> <div class="card extension-selector hoverable service-color" data-service="tesseract-ocr-pipeline"> - <a href="{{ url_for('.tesseract_ocr_pipeline_models.tesseract_ocr_pipeline_models') }}" style="position: absolute; width: 100%; height: 100%;"></a> + <a href="{{ url_for('.tesseract_ocr_pipeline_models') }}" style="position: absolute; width: 100%; height: 100%;"></a> <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> <p>Here you can see and edit the models that you have created. You can also create new models.</p> @@ -20,7 +20,7 @@ <div class="col s4"> <div class="card extension-selector hoverable service-color" data-service="spacy-nlp-pipeline"> - <a href="{{ url_for('.spacy_nlp_pipeline_models.spacy_nlp_pipeline_models') }}" style="position: absolute; width: 100%; height: 100%;"></a> + <a href="{{ url_for('.spacy_nlp_pipeline_models') }}" style="position: absolute; width: 100%; height: 100%;"></a> <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> <p>Here you can see and edit the models that you have created. You can also create new models.</p> diff --git a/app/templates/contributions/spacy_nlp_pipeline_models/create_spacy_nlp_pipeline_model.html.j2 b/app/templates/contributions/create_spacy_nlp_pipeline_model.html.j2 similarity index 100% rename from app/templates/contributions/spacy_nlp_pipeline_models/create_spacy_nlp_pipeline_model.html.j2 rename to app/templates/contributions/create_spacy_nlp_pipeline_model.html.j2 diff --git a/app/templates/contributions/tesseract_ocr_pipeline_models/create_tesseract_ocr_pipeline_model.html.j2 b/app/templates/contributions/create_tesseract_ocr_pipeline_model.html.j2 similarity index 100% rename from app/templates/contributions/tesseract_ocr_pipeline_models/create_tesseract_ocr_pipeline_model.html.j2 rename to app/templates/contributions/create_tesseract_ocr_pipeline_model.html.j2 diff --git a/app/templates/contributions/spacy_nlp_pipeline_models/spacy_nlp_pipeline_model.html.j2 b/app/templates/contributions/spacy_nlp_pipeline_model.html.j2 similarity index 100% rename from app/templates/contributions/spacy_nlp_pipeline_models/spacy_nlp_pipeline_model.html.j2 rename to app/templates/contributions/spacy_nlp_pipeline_model.html.j2 diff --git a/app/templates/contributions/spacy_nlp_pipeline_models/spacy_nlp_pipeline_models.html.j2 b/app/templates/contributions/spacy_nlp_pipeline_models.html.j2 similarity index 100% rename from app/templates/contributions/spacy_nlp_pipeline_models/spacy_nlp_pipeline_models.html.j2 rename to app/templates/contributions/spacy_nlp_pipeline_models.html.j2 diff --git a/app/templates/contributions/tesseract_ocr_pipeline_models/tesseract_ocr_pipeline_model.html.j2 b/app/templates/contributions/tesseract_ocr_pipeline_model.html.j2 similarity index 100% rename from app/templates/contributions/tesseract_ocr_pipeline_models/tesseract_ocr_pipeline_model.html.j2 rename to app/templates/contributions/tesseract_ocr_pipeline_model.html.j2 diff --git a/app/templates/contributions/tesseract_ocr_pipeline_models/tesseract_ocr_pipeline_models.html.j2 b/app/templates/contributions/tesseract_ocr_pipeline_models.html.j2 similarity index 100% rename from app/templates/contributions/tesseract_ocr_pipeline_models/tesseract_ocr_pipeline_models.html.j2 rename to app/templates/contributions/tesseract_ocr_pipeline_models.html.j2 diff --git a/app/templates/corpora/_breadcrumbs.html.j2 b/app/templates/corpora/_breadcrumbs.html.j2 deleted file mode 100644 index bdbe0f34..00000000 --- a/app/templates/corpora/_breadcrumbs.html.j2 +++ /dev/null @@ -1,28 +0,0 @@ -{# {% set breadcrumbs %} -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a href="{{ url_for('main.dashboard', _anchor='jobs') }}" target="_self">My corpora</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -{% if request.path == url_for('.create_corpus') %} -<li class="tab"><a class="active" href="{{ url_for('.create_corpus') }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.import_corpus') %} -<li class="tab"><a class="active" href="{{ url_for('.import_corpus') }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.corpus', corpus_id=corpus.id) %} -<li class="tab"><a class="active" href="{{ url_for('.corpus', corpus_id=corpus.id) }}" target="_self">{{ corpus.title }}</a></li> -{% elif request.path == url_for('.analyse_corpus', corpus_id=corpus.id) %} -<li class="tab"><a href="{{ url_for('.corpus', corpus_id=corpus.id) }}" target="_self">{{ corpus.title }}</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a class="active" href="{{ url_for('.analyse_corpus', corpus_id=corpus.id) }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.create_corpus_file', corpus_id=corpus.id) %} -<li class="tab"><a href="{{ url_for('.corpus', corpus_id=corpus.id) }}" target="_self">{{ corpus.title }}</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a href="{{ url_for('.corpus', corpus_id=corpus.id, _anchor='files') }}" target="_self">Corpus files</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a class="active" href="{{ url_for('.create_corpus_file', corpus_id=corpus.id) }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.corpus_file', corpus_file_id=corpus_file.id, corpus_id=corpus.id) %} -<li class="tab"><a href="{{ url_for('.corpus', corpus_id=corpus.id) }}" target="_self">{{ corpus.title }}</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a href="{{ url_for('.corpus', corpus_id=corpus.id, _anchor='files') }}" target="_self">Corpus files</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a class="active" href="{{ url_for('.corpus_file', corpus_file_id=corpus_file.id, corpus_id=corpus.id) }}" target="_self">{{ corpus_file.author }}: {{ corpus_file.title }} ({{ corpus_file.publishing_year }})</a></li> -{% endif %} -{% endset %} #} diff --git a/app/templates/corpora/corpus.html.j2 b/app/templates/corpora/corpus.html.j2 index 053dbdc0..f17e1e88 100644 --- a/app/templates/corpora/corpus.html.j2 +++ b/app/templates/corpora/corpus.html.j2 @@ -1,6 +1,5 @@ {% extends "base.html.j2" %} {% import "materialize/wtf.html.j2" as wtf %} -{% from "corpora/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %} diff --git a/app/templates/corpora/create_corpus.html.j2 b/app/templates/corpora/create_corpus.html.j2 index ccb49877..5feb7225 100644 --- a/app/templates/corpora/create_corpus.html.j2 +++ b/app/templates/corpora/create_corpus.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "corpora/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %} diff --git a/app/templates/corpora/files/corpus_file.html.j2 b/app/templates/corpora/files/corpus_file.html.j2 index 781ea059..28a762d4 100644 --- a/app/templates/corpora/files/corpus_file.html.j2 +++ b/app/templates/corpora/files/corpus_file.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "corpora/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %} diff --git a/app/templates/corpora/files/create_corpus_file.html.j2 b/app/templates/corpora/files/create_corpus_file.html.j2 index 8cff13a6..ff634294 100644 --- a/app/templates/corpora/files/create_corpus_file.html.j2 +++ b/app/templates/corpora/files/create_corpus_file.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "corpora/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %} diff --git a/app/templates/corpora/import_corpus.html.j2 b/app/templates/corpora/import_corpus.html.j2 index 957668e6..dd0806a7 100644 --- a/app/templates/corpora/import_corpus.html.j2 +++ b/app/templates/corpora/import_corpus.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "corpora/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %} diff --git a/app/templates/jobs/_breadcrumbs.html.j2 b/app/templates/jobs/_breadcrumbs.html.j2 deleted file mode 100644 index e3de43f3..00000000 --- a/app/templates/jobs/_breadcrumbs.html.j2 +++ /dev/null @@ -1,8 +0,0 @@ -{% set breadcrumbs %} -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a href="{{ url_for('main.dashboard', _anchor='jobs') }}" target="_self">My jobs</a></li> -{% if request.path == url_for('.job', job_id=job.id) %} -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a class="active" href="{{ url_for('.job', job_id=job.id) }}" target="_self">{{ job.title }}</a></li> -{% endif %} -{% endset %} diff --git a/app/templates/jobs/job.html.j2 b/app/templates/jobs/job.html.j2 index b5055ce6..51f2fc6d 100644 --- a/app/templates/jobs/job.html.j2 +++ b/app/templates/jobs/job.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "jobs/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block main_attribs %} class="service-scheme" data-service="{{ job.service }}"{% endblock main_attribs %} diff --git a/app/templates/main/_breadcrumbs.html.j2 b/app/templates/main/_breadcrumbs.html.j2 deleted file mode 100644 index d65978d6..00000000 --- a/app/templates/main/_breadcrumbs.html.j2 +++ /dev/null @@ -1,14 +0,0 @@ -{% set breadcrumbs %} -{% if not (request.path == url_for('.index')) %} -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -{% endif %} -{% if request.path == url_for('.faq') %} -<li class="tab"><a class="active" href="{{ url_for('.faq') }}" target="_self">Frequently Asked Questions</a></li> -{% elif request.path == url_for('.dashboard') %} -<li class="tab"><a class="active" href="{{ url_for('.dashboard') }}" target="_self">Dashboard</a></li> -{% elif request.path == url_for('.news') %} -<li class="tab"><a class="active" href="{{ url_for('.news') }}" target="_self">News</a></li> -{% elif request.path == url_for('.terms_of_use') %} -<li class="tab"><a class="active" href="{{ url_for('.terms_of_use') }}" target="_self">Terms of use</a></li> -{% endif %} -{% endset %} diff --git a/app/templates/main/dashboard.html.j2 b/app/templates/main/dashboard.html.j2 index 5474db97..1598e139 100644 --- a/app/templates/main/dashboard.html.j2 +++ b/app/templates/main/dashboard.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "main/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block page_content %} <div class="container"> diff --git a/app/templates/main/faq.html.j2 b/app/templates/main/faq.html.j2 index fb10e3b4..5c168007 100644 --- a/app/templates/main/faq.html.j2 +++ b/app/templates/main/faq.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "main/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block page_content %} <div class="container"> diff --git a/app/templates/main/index.html.j2 b/app/templates/main/index.html.j2 index 74bfa306..7b80a794 100644 --- a/app/templates/main/index.html.j2 +++ b/app/templates/main/index.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "main/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block page_content %} diff --git a/app/templates/main/news.html.j2 b/app/templates/main/news.html.j2 index b8961b02..d1111d8b 100644 --- a/app/templates/main/news.html.j2 +++ b/app/templates/main/news.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "main/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block page_content %} <div class="container"> diff --git a/app/templates/main/news_new.html.j2 b/app/templates/main/news_new.html.j2 index d3e2f9b6..d2b339e3 100644 --- a/app/templates/main/news_new.html.j2 +++ b/app/templates/main/news_new.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "main/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block page_content %} <div class="container"> diff --git a/app/templates/main/privacy_policy.html.j2 b/app/templates/main/privacy_policy.html.j2 index ff22b10a..c55ae722 100644 --- a/app/templates/main/privacy_policy.html.j2 +++ b/app/templates/main/privacy_policy.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "main/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block page_content %} <div class="container"> diff --git a/app/templates/main/terms_of_use.html.j2 b/app/templates/main/terms_of_use.html.j2 index baedd0a2..d61084d6 100644 --- a/app/templates/main/terms_of_use.html.j2 +++ b/app/templates/main/terms_of_use.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "main/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block page_content %} <div class="container"> diff --git a/app/templates/main/user_manual.html.j2 b/app/templates/main/user_manual.html.j2 index 0da85809..2ef1da01 100644 --- a/app/templates/main/user_manual.html.j2 +++ b/app/templates/main/user_manual.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "main/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block page_content %} <div class="container"> diff --git a/app/templates/services/_breadcrumbs.html.j2 b/app/templates/services/_breadcrumbs.html.j2 deleted file mode 100644 index a08beafa..00000000 --- a/app/templates/services/_breadcrumbs.html.j2 +++ /dev/null @@ -1,16 +0,0 @@ -{% set breadcrumbs %} -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -<li class="tab"><a href="{{ url_for('main.index', _anchor='services') }}" target="_self">Processes & Services</a></li> -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -{% if request.path == url_for('.corpus_analysis') %} -<li class="tab"><a class="active" href="{{ url_for('.corpus_analysis') }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.file_setup_pipeline') %} -<li class="tab"><a class="active" href="{{ url_for('.file_setup_pipeline') }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.spacy_nlp_pipeline') %} -<li class="tab"><a class="active" href="{{ url_for('.spacy_nlp_pipeline') }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.tesseract_ocr_pipeline') %} -<li class="tab"><a class="active" href="{{ url_for('.tesseract_ocr_pipeline') }}" target="_self">{{ title }}</a></li> -{% elif request.path == url_for('.transkribus_htr_pipeline') %} -<li class="tab"><a class="active" href="{{ url_for('.transkribus_htr_pipeline') }}" target="_self">{{ title }}</a></li> -{% endif %} -{% endset %} diff --git a/app/templates/services/corpus_analysis.html.j2 b/app/templates/services/corpus_analysis.html.j2 index a7e3da8e..9ddc9ec3 100644 --- a/app/templates/services/corpus_analysis.html.j2 +++ b/app/templates/services/corpus_analysis.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "services/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %} diff --git a/app/templates/services/file_setup_pipeline.html.j2 b/app/templates/services/file_setup_pipeline.html.j2 index ebc4cfc4..0f046e98 100644 --- a/app/templates/services/file_setup_pipeline.html.j2 +++ b/app/templates/services/file_setup_pipeline.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "services/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block main_attribs %} class="service-scheme" data-service="file-setup-pipeline"{% endblock main_attribs %} diff --git a/app/templates/services/spacy_nlp_pipeline.html.j2 b/app/templates/services/spacy_nlp_pipeline.html.j2 index 8f466e3d..2ebba838 100644 --- a/app/templates/services/spacy_nlp_pipeline.html.j2 +++ b/app/templates/services/spacy_nlp_pipeline.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "services/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block main_attribs %} class="service-scheme" data-service="spacy-nlp-pipeline"{% endblock main_attribs %} @@ -77,7 +76,7 @@ {{ form.model.label }} <span class="helper-text"> <a class="modal-trigger tooltipped" href="#models-modal" data-position="bottom" data-tooltip="See more information about models"><i class="material-icons" style="color:#0064A3;">help_outline</i></a> - <a class="tooltipped" href="{{ url_for('contributions.spacy_nlp_pipeline_models.create_spacy_nlp_pipeline_model') }}" data-position="bottom" data-tooltip="Add your own spaCy NLP models"><i class="material-icons" style="color:#0064A3">new_label</i></a> + <a class="tooltipped" href="{{ url_for('contributions.create_spacy_nlp_pipeline_model') }}" data-position="bottom" data-tooltip="Add your own spaCy NLP models"><i class="material-icons" style="color:#0064A3">new_label</i></a> </span> </div> </div> diff --git a/app/templates/services/tesseract_ocr_pipeline.html.j2 b/app/templates/services/tesseract_ocr_pipeline.html.j2 index 11d65c64..9b1d6187 100644 --- a/app/templates/services/tesseract_ocr_pipeline.html.j2 +++ b/app/templates/services/tesseract_ocr_pipeline.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "services/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block main_attribs %} class="service-scheme" data-service="tesseract-ocr-pipeline"{% endblock main_attribs %} @@ -59,7 +58,7 @@ {{ form.model.label }} <span class="helper-text"> <a class="modal-trigger tooltipped" href="#models-modal" data-position="bottom" data-tooltip="See more information about models"><i class="material-icons" style="color:#00A58B;">help_outline</i></a> - <a class="tooltipped" href="{{ url_for('contributions.tesseract_ocr_pipeline_models.create_tesseract_ocr_pipeline_model') }}" data-position="bottom" data-tooltip="Add your own Tesseract OCR models"><i class="material-icons" style="color:#00A58B">new_label</i></a> + <a class="tooltipped" href="{{ url_for('contributions.create_tesseract_ocr_pipeline_model') }}" data-position="bottom" data-tooltip="Add your own Tesseract OCR models"><i class="material-icons" style="color:#00A58B">new_label</i></a> </span> {% for error in form.model.errors %} <span class="helper-text error-color-text">{{ error }}</span> diff --git a/app/templates/services/transkribus_htr_pipeline.html.j2 b/app/templates/services/transkribus_htr_pipeline.html.j2 index f5468ce9..50eb3b6e 100644 --- a/app/templates/services/transkribus_htr_pipeline.html.j2 +++ b/app/templates/services/transkribus_htr_pipeline.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "services/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block main_attribs %} class="service-scheme" data-service="transkribus-htr-pipeline"{% endblock main_attribs %} diff --git a/app/templates/settings/_breadcrumbs.html.j2 b/app/templates/settings/_breadcrumbs.html.j2 deleted file mode 100644 index 3b5077bf..00000000 --- a/app/templates/settings/_breadcrumbs.html.j2 +++ /dev/null @@ -1,6 +0,0 @@ -{% set breadcrumbs %} -<li class="tab disabled"><i class="material-icons">navigate_next</i></li> -{% if request.path == url_for('settings.settings') %} -<li class="tab"><a{%if request.path == url_for('settings.settings') %} class="active"{% endif %} href="{{ url_for('settings.settings') }}" target="_self">Settings</a></li> -{% endif %} -{% endset %} diff --git a/app/templates/settings/settings.html.j2 b/app/templates/settings/settings.html.j2 index e9cfb97b..fd830aee 100644 --- a/app/templates/settings/settings.html.j2 +++ b/app/templates/settings/settings.html.j2 @@ -1,5 +1,4 @@ {% extends "base.html.j2" %} -{% from "settings/_breadcrumbs.html.j2" import breadcrumbs with context %} {% import "materialize/wtf.html.j2" as wtf %} {% block page_content %} diff --git a/requirements.txt b/requirements.txt index 838cee8f..e621d2ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ eventlet Flask==2.1.3 Flask-APScheduler Flask-Assets +Flask-Breadcrumbs Flask-Hashids==1.0.1 Flask-HTTPAuth Flask-Login -- GitLab