diff --git a/.gitignore b/.gitignore index 76c4e06b..14a22fe1 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ __pycache__ # Virtual environment venv +.idea diff --git a/app/contributions/forms.py b/app/contributions/forms.py new file mode 100644 index 00000000..d123e6d1 --- /dev/null +++ b/app/contributions/forms.py @@ -0,0 +1,58 @@ +from flask_wtf import FlaskForm +from flask_wtf.file import FileField, FileRequired +from wtforms import ( + BooleanField, + StringField, + SubmitField, + SelectMultipleField, + IntegerField +) +from wtforms.validators import InputRequired, Length +from app.services import SERVICES + + +class ContributionForm(FlaskForm): + title = StringField( + 'Title', + validators=[InputRequired(), Length(max=64)] + ) + description = StringField( + 'Description', + validators=[InputRequired(), Length(max=255)] + ) + version = StringField( + 'Version', + validators=[InputRequired(), Length(max=16)] + ) + compatible_service_versions = SelectMultipleField( + 'Compatible service versions' + ) + publisher = StringField( + 'Publisher', + validators=[InputRequired(), Length(max=128)] + ) + publisher_url = StringField( + 'Publisher URL', + validators=[InputRequired(), Length(max=512)] + ) + publishing_url = StringField( + 'Publishing URL', + validators=[InputRequired(), Length(max=512)] + ) + publishing_year = IntegerField( + 'Publishing year', + validators=[InputRequired()] + ) + shared = BooleanField('Shared', validators=[InputRequired()]) + model_file = FileField('File',validators=[FileRequired()]) + submit = SubmitField() + + + 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/routes.py b/app/contributions/routes.py index 80c6a82d..d82bf511 100644 --- a/app/contributions/routes.py +++ b/app/contributions/routes.py @@ -1,7 +1,10 @@ +from flask import abort, flash, Markup, render_template, url_for from flask_login import login_required +from app import db from app.decorators import permission_required -from app.models import Permission +from app.models import TesseractOCRModel, Permission from . import bp +from .forms import ContributionForm @bp.before_request @@ -14,3 +17,38 @@ def before_request(): @bp.route('') def contributions(): pass + + +@bp.route('/tesseract-ocr-pipeline-models', methods=['GET', 'POST']) +def tesseract_ocr_pipeline_models(): + form = ContributionForm( + prefix='contribute-tesseract-ocr-pipeline-model-form' + ) + if form.is_submitted(): + if not form.validate(): + response = {'errors': form.errors} + return response, 400 + try: + tesseract_ocr_model = TesseractOCRModel.create( + form.file.data, + compatible_service_versions=form.compatible_service_versions.data, + description=form.description.data, + publisher=form.publisher.data, + publisher_url=form.publisher_url.data, + publishing_url=form.publishing_url.data, + publishing_year=form.publishing_year.data, + shared=form.shared.data, + title=form.title.data, + version=form.version.data + ) + except OSError: + abort(500) + db.session.commit() + message = Markup(f'Model "{tesseract_ocr_model.title}" created') + flash(message) + return {}, 201, {'Location': url_for('contributions.contributions')} + return render_template( + 'contributions/contribute.html.j2', + form=form, + title='Contribution' + ) diff --git a/app/models.py b/app/models.py index cc5d60ce..c0cedf21 100644 --- a/app/models.py +++ b/app/models.py @@ -620,6 +620,55 @@ class TesseractOCRPipelineModel(FileMixin, HashidMixin, db.Model): _json['user'] = self.user.to_json(backrefs=True) return _json + @staticmethod + def create(model_file, **kwargs): # ??? Ist das so richtig? übergeben wir das form? Im anderen Fall ist das nicht so .. aber was soll hier überhaupt rein? + filename = kwargs.get('filename', model_file.filename) + mimetype = kwargs.get('mimetype', model_file.mimetype) + tesseract_ocr_model = TesseractOCRModel( + filename=secure_filename(filename), + mimetype=mimetype, + **kwargs + ) + + db.session.add(tesseract_ocr_model) + db.session.flush(objects=[tesseract_ocr_model]) + db.session.refresh(tesseract_ocr_model) + try: + # ??? Woher soll file kommen? Wir haben der methode das ganze form übergeben. + # filename = form.file.data.filename + model_file.save(tesseract_ocr_model.path) + except OSError as e: + current_app.logger.error(e) + db.session.rollback() + raise e + return tesseract_ocr_model + + + # Kann man das so committen? + # db.session.add(model) + # db.session.flush(objects=[model]) + # db.session.refresh(model) + # model.filename = f'{model.id}.traineddata' + # r = requests.get(m['url'], stream=True) + # pbar = tqdm( + # desc=f'{model.title} ({model.filename})', + # unit="B", + # unit_scale=True, + # unit_divisor=1024, + # total=int(r.headers['Content-Length']) + # ) + # pbar.clear() + # with open(model.path, 'wb') as f: + # for chunk in r.iter_content(chunk_size=1024): + # if chunk: # filter out keep-alive new chunks + # pbar.update(len(chunk)) + # f.write(chunk) + # pbar.close() + # + # db.session.commit() + + + class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model): __tablename__ = 'spacy_nlp_pipeline_models' diff --git a/app/templates/contributions/contribute.html.j2 b/app/templates/contributions/contribute.html.j2 new file mode 100644 index 00000000..6789e1f8 --- /dev/null +++ b/app/templates/contributions/contribute.html.j2 @@ -0,0 +1,32 @@ +{% extends "base.html.j2" %} +{% import "materialize/wtf.html.j2" as wtf %} + + +{% block page_content %} +
+ In order to add a new model, please fill in the form below. +
+ + +