add all files for last commit

This commit is contained in:
Patrick Jentsch 2022-11-29 15:28:10 +01:00
parent 5864aa653e
commit 5491cb184a
8 changed files with 130 additions and 44 deletions

View File

@ -55,7 +55,6 @@ class CreateTesseractOCRPipelineModelForm(ContributionBaseForm):
) )
def validate_tesseract_model_file(self, field): def validate_tesseract_model_file(self, field):
current_app.logger.warning(field.data.filename)
if not field.data.filename.lower().endswith('.traineddata'): if not field.data.filename.lower().endswith('.traineddata'):
raise ValidationError('traineddata files only!') raise ValidationError('traineddata files only!')
@ -80,7 +79,6 @@ class CreateSpaCyNLPPipelineModelForm(ContributionBaseForm):
) )
def validate_spacy_model_file(self, field): def validate_spacy_model_file(self, field):
current_app.logger.warning(field.data.filename)
if not field.data.filename.lower().endswith('.tar.gz'): if not field.data.filename.lower().endswith('.tar.gz'):
raise ValidationError('.tar.gz files only!') raise ValidationError('.tar.gz files only!')

View File

@ -104,7 +104,7 @@ def create_tesseract_ocr_pipeline_model():
publisher_url=form.publisher_url.data, publisher_url=form.publisher_url.data,
publishing_url=form.publishing_url.data, publishing_url=form.publishing_url.data,
publishing_year=form.publishing_year.data, publishing_year=form.publishing_year.data,
shared=False, is_public=False,
title=form.title.data, title=form.title.data,
version=form.version.data, version=form.version.data,
user=current_user user=current_user
@ -131,7 +131,7 @@ def toggle_tesseract_ocr_pipeline_model_public_status(tesseract_ocr_pipeline_mod
tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id) 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()): if not (tesseract_ocr_pipeline_model.user == current_user or current_user.is_administrator()):
abort(403) abort(403)
tesseract_ocr_pipeline_model.shared = not tesseract_ocr_pipeline_model.shared tesseract_ocr_pipeline_model.is_public = not tesseract_ocr_pipeline_model.is_public
db.session.commit() db.session.commit()
return {}, 201 return {}, 201
@ -201,7 +201,7 @@ def create_spacy_nlp_pipeline_model():
publisher_url=form.publisher_url.data, publisher_url=form.publisher_url.data,
publishing_url=form.publishing_url.data, publishing_url=form.publishing_url.data,
publishing_year=form.publishing_year.data, publishing_year=form.publishing_year.data,
shared=False, is_public=False,
title=form.title.data, title=form.title.data,
version=form.version.data, version=form.version.data,
user=current_user user=current_user
@ -228,6 +228,6 @@ def toggle_spacy_nlp_pipeline_model_public_status(spacy_nlp_pipeline_model_id):
spacy_nlp_pipeline_model = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id) 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()): if not (spacy_nlp_pipeline_model.user == current_user or current_user.is_administrator()):
abort(403) abort(403)
spacy_nlp_pipeline_model.shared = not spacy_nlp_pipeline_model.shared spacy_nlp_pipeline_model.is_public = not spacy_nlp_pipeline_model.is_public
db.session.commit() db.session.commit()
return {}, 201 return {}, 201

View File

@ -1,11 +1,17 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired from flask_wtf.file import FileField, FileRequired
from wtforms import StringField, SubmitField, ValidationError, IntegerField from wtforms import (
StringField,
SubmitField,
TextAreaField,
ValidationError,
IntegerField
)
from wtforms.validators import InputRequired, Length from wtforms.validators import InputRequired, Length
class CreateCorpusForm(FlaskForm): class CorpusBaseForm(FlaskForm):
description = StringField( description = TextAreaField(
'Description', 'Description',
validators=[InputRequired(), Length(max=255)] validators=[InputRequired(), Length(max=255)]
) )
@ -13,6 +19,20 @@ class CreateCorpusForm(FlaskForm):
submit = SubmitField() submit = SubmitField()
class CreateCorpusForm(CorpusBaseForm):
def __init__(self, *args, **kwargs):
if 'prefix' not in kwargs:
kwargs['prefix'] = 'create-corpus-form'
super().__init__(*args, **kwargs)
class UpdateCorpusForm(CorpusBaseForm):
def __init__(self, *args, **kwargs):
if 'prefix' not in kwargs:
kwargs['prefix'] = 'update-corpus-form'
super().__init__(*args, **kwargs)
class CorpusFileBaseForm(FlaskForm): class CorpusFileBaseForm(FlaskForm):
author = StringField( author = StringField(
'Author', 'Author',
@ -41,13 +61,21 @@ class CorpusFileBaseForm(FlaskForm):
class CreateCorpusFileForm(CorpusFileBaseForm): class CreateCorpusFileForm(CorpusFileBaseForm):
vrt = FileField('File', validators=[FileRequired()]) vrt = FileField('File', validators=[FileRequired()])
def __init__(self, *args, **kwargs):
if 'prefix' not in kwargs:
kwargs['prefix'] = 'create-corpus-file-form'
super().__init__(*args, **kwargs)
def validate_vrt(self, field): def validate_vrt(self, field):
if not field.data.filename.lower().endswith('.vrt'): if not field.data.filename.lower().endswith('.vrt'):
raise ValidationError('VRT files only!') raise ValidationError('VRT files only!')
class EditCorpusFileForm(CorpusFileBaseForm): class UpdateCorpusFileForm(CorpusFileBaseForm):
pass def __init__(self, *args, **kwargs):
if 'prefix' not in kwargs:
kwargs['prefix'] = 'update-corpus-file-form'
super().__init__(*args, **kwargs)
class ImportCorpusForm(FlaskForm): class ImportCorpusForm(FlaskForm):

View File

@ -13,7 +13,26 @@ import os
from app import db from app import db
from app.models import Corpus, CorpusFile, CorpusStatus from app.models import Corpus, CorpusFile, CorpusStatus
from . import bp from . import bp
from .forms import CreateCorpusFileForm, CreateCorpusForm, EditCorpusFileForm from .forms import CreateCorpusFileForm, CreateCorpusForm, UpdateCorpusFileForm
def user_can_read_corpus(user, corpus):
return corpus.user == user or user.is_administrator() or corpus.is_public
def user_can_update_corpus(user, corpus):
return corpus.user == user or user.is_administrator()
def user_can_delete_corpus(user, corpus):
return user_can_update_corpus(user, corpus)
@bp.route('')
@login_required
def corpora():
corpora = Corpus.query.filter(Corpus.user_id == current_user.id | Corpus.is_public == True).all()
return render_template('corpora/corpora.html', corpora=corpora)
@bp.route('/create', methods=['GET', 'POST']) @bp.route('/create', methods=['GET', 'POST'])
@ -46,7 +65,7 @@ def create_corpus():
@login_required @login_required
def corpus(corpus_id): def corpus(corpus_id):
corpus = Corpus.query.get_or_404(corpus_id) corpus = Corpus.query.get_or_404(corpus_id)
if not (corpus.user == current_user or current_user.is_administrator()): if not user_can_read_corpus(current_user, corpus):
abort(403) abort(403)
return render_template( return render_template(
'corpora/corpus.html.j2', 'corpora/corpus.html.j2',
@ -55,6 +74,19 @@ def corpus(corpus_id):
) )
# @bp.route('/<hashid:corpus_id>/update')
# @login_required
# def update_corpus(corpus_id):
# corpus = Corpus.query.get_or_404(corpus_id)
# if not user_can_update_corpus(current_user, corpus):
# abort(403)
# return render_template(
# 'corpora/update_corpus.html.j2',
# corpus=corpus,
# title='Corpus'
# )
@bp.route('/<hashid:corpus_id>', methods=['DELETE']) @bp.route('/<hashid:corpus_id>', methods=['DELETE'])
@login_required @login_required
def delete_corpus(corpus_id): def delete_corpus(corpus_id):
@ -65,7 +97,7 @@ def delete_corpus(corpus_id):
db.session.commit() db.session.commit()
corpus = Corpus.query.get_or_404(corpus_id) corpus = Corpus.query.get_or_404(corpus_id)
if not (corpus.user == current_user or current_user.is_administrator()): if not user_can_delete_corpus(current_user, corpus):
abort(403) abort(403)
thread = Thread( thread = Thread(
target=_delete_corpus, target=_delete_corpus,
@ -79,6 +111,8 @@ def delete_corpus(corpus_id):
@login_required @login_required
def analyse_corpus(corpus_id): def analyse_corpus(corpus_id):
corpus = Corpus.query.get_or_404(corpus_id) corpus = Corpus.query.get_or_404(corpus_id)
if not user_can_read_corpus(current_user, corpus):
abort(403)
return render_template( return render_template(
'corpora/analyse_corpus.html.j2', 'corpora/analyse_corpus.html.j2',
corpus=corpus, corpus=corpus,
@ -96,7 +130,7 @@ def build_corpus(corpus_id):
db.session.commit() db.session.commit()
corpus = Corpus.query.get_or_404(corpus_id) corpus = Corpus.query.get_or_404(corpus_id)
if not (corpus.user == current_user or current_user.is_administrator()): if not user_can_update_corpus(current_user, corpus):
abort(403) abort(403)
# Check if the corpus has corpus files # Check if the corpus has corpus files
if not corpus.files.all(): if not corpus.files.all():
@ -114,7 +148,7 @@ def build_corpus(corpus_id):
@login_required @login_required
def create_corpus_file(corpus_id): def create_corpus_file(corpus_id):
corpus = Corpus.query.get_or_404(corpus_id) corpus = Corpus.query.get_or_404(corpus_id)
if not (corpus.user == current_user or current_user.is_administrator()): if not user_can_update_corpus(current_user, corpus):
abort(403) abort(403)
form = CreateCorpusFileForm(prefix='create-corpus-file-form') form = CreateCorpusFileForm(prefix='create-corpus-file-form')
if form.is_submitted(): if form.is_submitted():
@ -157,16 +191,13 @@ def create_corpus_file(corpus_id):
) )
@bp.route('/<hashid:corpus_id>/files/<hashid:corpus_file_id>', @bp.route('/<hashid:corpus_id>/files/<hashid:corpus_file_id>', methods=['GET', 'POST'])
methods=['GET', 'POST'])
@login_required @login_required
def corpus_file(corpus_id, corpus_file_id): def corpus_file(corpus_id, corpus_file_id):
corpus_file = CorpusFile.query.get_or_404(corpus_file_id) corpus_file = CorpusFile.query.filter_by(corpus_id = corpus_id, id=corpus_file_id).first_or_404()
if corpus_file.corpus.id != corpus_id:
abort(404)
if not (corpus_file.corpus.user == current_user or current_user.is_administrator()): if not (corpus_file.corpus.user == current_user or current_user.is_administrator()):
abort(403) abort(403)
form = EditCorpusFileForm( form = UpdateCorpusFileForm(
data=corpus_file.to_json_serializeable(), data=corpus_file.to_json_serializeable(),
prefix='edit-corpus-file-form' prefix='edit-corpus-file-form'
) )
@ -196,9 +227,7 @@ def delete_corpus_file(corpus_id, corpus_file_id):
corpus_file.delete() corpus_file.delete()
db.session.commit() db.session.commit()
corpus_file = CorpusFile.query.get_or_404(corpus_file_id) corpus_file = CorpusFile.query.filter_by(corpus_id = corpus_id, id=corpus_file_id).first_or_404()
if corpus_file.corpus.id != corpus_id:
abort(404)
if not (corpus_file.corpus.user == current_user or current_user.is_administrator()): if not (corpus_file.corpus.user == current_user or current_user.is_administrator()):
abort(403) abort(403)
thread = Thread( thread = Thread(
@ -212,9 +241,7 @@ def delete_corpus_file(corpus_id, corpus_file_id):
@bp.route('/<hashid:corpus_id>/files/<hashid:corpus_file_id>/download') @bp.route('/<hashid:corpus_id>/files/<hashid:corpus_file_id>/download')
@login_required @login_required
def download_corpus_file(corpus_id, corpus_file_id): def download_corpus_file(corpus_id, corpus_file_id):
corpus_file = CorpusFile.query.get_or_404(corpus_file_id) corpus_file = CorpusFile.query.filter_by(corpus_id = corpus_id, id=corpus_file_id).first_or_404()
if corpus_file.corpus.id != corpus_id:
abort(404)
if not (corpus_file.corpus.user == current_user or current_user.is_administrator()): if not (corpus_file.corpus.user == current_user or current_user.is_administrator()):
abort(403) abort(403)
return send_from_directory( return send_from_directory(

View File

@ -46,13 +46,13 @@
} }
// Set the data-length attribute on inputs with the maxlength attribute // Set the data-length attribute on inputs with the maxlength attribute
for (let inputElement of document.querySelectorAll('input[maxlength]')) { for (let inputElement of document.querySelectorAll('input[maxlength], textarea[maxlength]')) {
inputElement.dataset.length = inputElement.getAttribute('maxlength'); inputElement.dataset.length = inputElement.getAttribute('maxlength');
} }
// Initialize components // Initialize components
M.AutoInit(); M.AutoInit();
M.CharacterCounter.init(document.querySelectorAll('input[data-length][type="text"], input[data-length][type="email"], input[data-length][type="search"], input[data-length][type="password"], input[data-length][type="tel"], input[data-length][type="url"]')); M.CharacterCounter.init(document.querySelectorAll('input[data-length][type="text"], input[data-length][type="email"], input[data-length][type="search"], input[data-length][type="password"], input[data-length][type="tel"], input[data-length][type="url"], textarea[data-length]'));
M.Dropdown.init( M.Dropdown.init(
document.querySelectorAll('#nav-more-dropdown-trigger'), document.querySelectorAll('#nav-more-dropdown-trigger'),
{alignment: 'right', constrainWidth: false, coverTrigger: false} {alignment: 'right', constrainWidth: false, coverTrigger: false}

View File

@ -9,28 +9,19 @@
<div class="row"> <div class="row">
<div class="col s12"> <div class="col s12">
<h1 id="title">{{ title }}</h1> <h1 id="title">{{ title }}</h1>
<p>Here you can create a new corpus, just choose a title and a description which will help to identify it later. After the corpus has been created, annotated texts in <i>verticalized text format</i> can be added to it on the corpus overview page.</p>
</div> </div>
<div class="col s12 m4"> <div class="col s12">
<p>Fill out the following form to add a corpus to your corpora.</p>
<a class="waves-effect waves-light btn" href="{{ url_for('main.dashboard') }}"><i class="material-icons left">arrow_back</i>Back to dashboard</a>
</div>
<div class="col s12 m8">
<div class="card"> <div class="card">
<form method="POST"> <form method="POST">
<div class="card-content"> <div class="card-content">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="row">
<div class="col s12 m4">
{{ wtf.render_field(form.title, material_icon='title') }} {{ wtf.render_field(form.title, material_icon='title') }}
</div>
<div class="col s12 m8">
{{ wtf.render_field(form.description, material_icon='description') }} {{ wtf.render_field(form.description, material_icon='description') }}
</div> </div>
</div>
</div>
<div class="card-action right-align"> <div class="card-action right-align">
<a class="waves-effect waves-light btn red" href="{{ url_for('main.dashboard', _anchor='corpora') }}"><i class="material-icons left">close</i>Cancel</a>
{{ wtf.render_field(form.submit, material_icon='send') }} {{ wtf.render_field(form.submit, material_icon='send') }}
</div> </div>
</form> </form>

View File

@ -36,6 +36,7 @@
</div> </div>
</div> </div>
<div class="card-action right-align"> <div class="card-action right-align">
<a class="waves-effect waves-light btn red" href="{{ url_for('.corpus', corpus_id=corpus.id) }}"><i class="material-icons left">close</i>Cancel</a>
{{ wtf.render_field(form.submit, material_icon='send') }} {{ wtf.render_field(form.submit, material_icon='send') }}
</div> </div>
</div> </div>

View File

@ -0,0 +1,41 @@
"""empty message
Revision ID: f2656133df2f
Revises: 31b9c0259e6b
Create Date: 2022-11-29 14:17:16.845501
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f2656133df2f'
down_revision = '31b9c0259e6b'
branch_labels = None
depends_on = None
def upgrade():
op.add_column(
'corpus_files',
sa.Column('description', sa.String(length=255), nullable=True)
)
op.alter_column(
'spacy_nlp_pipeline_models',
'shared',
new_column_name='is_public'
)
op.alter_column(
'tesseract_ocr_pipeline_models',
'shared',
new_column_name='is_public'
)
def downgrade():
op.alter_column('tesseract_ocr_pipeline_models', 'is_public', new_column_name='shared')
op.alter_column('spacy_nlp_pipeline_models', 'is_public', new_column_name='shared')
op.drop_column('corpus_files', 'description')