From 268da220d2160ccba46eb718463d8b91b2438fe7 Mon Sep 17 00:00:00 2001 From: Patrick Jentsch Date: Wed, 10 Apr 2024 13:34:48 +0200 Subject: [PATCH] Enhance code structure --- app/__init__.py | 3 + app/admin/json_routes.py | 2 +- app/admin/routes.py | 4 +- app/api/jobs.py | 2 +- app/api/users.py | 4 +- .../spacy_nlp_pipeline_models/json_routes.py | 2 +- .../spacy_nlp_pipeline_models/routes.py | 4 +- .../tesseract_ocr_pipeline_models/routes.py | 4 +- app/converters/sandpaper.py | 6 +- app/corpora/files/json_routes.py | 4 +- app/corpora/files/utils.py | 1 - app/ext/flask_sqlalchemy/__init__.py | 2 - app/ext/flask_sqlalchemy/container_column.py | 21 ------- app/extensions/__init__.py | 1 + app/extensions/sqlalchemy/__init__.py | 2 + .../sqlalchemy/types.py} | 20 +++++++ app/{ => extensions}/wtforms/__init__.py | 0 app/{ => extensions}/wtforms/validators.py | 0 app/models/corpus.py | 2 +- app/models/job.py | 2 +- app/models/spacy_nlp_pipeline_model.py | 2 +- app/models/tesseract_ocr_pipeline_model.py | 2 +- app/models/user.py | 2 +- app/static/js/forms/base-form-new.js | 57 +++++++++++++++++++ app/users/settings/forms.py | 3 +- requirements.txt | 2 +- 26 files changed, 104 insertions(+), 50 deletions(-) delete mode 100644 app/ext/flask_sqlalchemy/__init__.py delete mode 100644 app/ext/flask_sqlalchemy/container_column.py create mode 100644 app/extensions/__init__.py create mode 100644 app/extensions/sqlalchemy/__init__.py rename app/{ext/flask_sqlalchemy/int_enum_column.py => extensions/sqlalchemy/types.py} (52%) rename app/{ => extensions}/wtforms/__init__.py (100%) rename app/{ => extensions}/wtforms/validators.py (100%) create mode 100644 app/static/js/forms/base-form-new.js diff --git a/app/__init__.py b/app/__init__.py index 43648294..3b8d8a15 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -33,6 +33,9 @@ scheduler = APScheduler() socketio = SocketIO() +# TODO: Create export for lemmatized corpora + + def create_app(config: Config = Config) -> Flask: ''' Creates an initialized Flask (WSGI Application) object. ''' app = Flask(__name__) diff --git a/app/admin/json_routes.py b/app/admin/json_routes.py index 9b4ca7d0..e120a654 100644 --- a/app/admin/json_routes.py +++ b/app/admin/json_routes.py @@ -1,6 +1,6 @@ from flask import abort, request -from app import db from app.decorators import content_negotiation +from app import db from app.models import User from . import bp diff --git a/app/admin/routes.py b/app/admin/routes.py index 66d02f00..a1f5e455 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -9,12 +9,12 @@ from app.users.settings.forms import ( UpdateAccountInformationForm, UpdateProfileInformationForm ) -from . import bp -from .forms import UpdateUserForm from app.users.utils import ( user_endpoint_arguments_constructor as user_eac, user_dynamic_list_constructor as user_dlc ) +from . import bp +from .forms import UpdateUserForm @bp.route('') diff --git a/app/api/jobs.py b/app/api/jobs.py index 2eaecd3f..97c0a98a 100644 --- a/app/api/jobs.py +++ b/app/api/jobs.py @@ -5,8 +5,8 @@ from flask import abort, Blueprint from werkzeug.exceptions import InternalServerError from app import db, hashids from app.models import Job, JobInput, JobStatus, TesseractOCRPipelineModel -from .schemas import EmptySchema, JobSchema, SpaCyNLPPipelineJobSchema, TesseractOCRPipelineJobSchema, TesseractOCRPipelineModelSchema from .auth import auth_error_responses, token_auth +from .schemas import EmptySchema, JobSchema, SpaCyNLPPipelineJobSchema, TesseractOCRPipelineJobSchema, TesseractOCRPipelineModelSchema bp = Blueprint('jobs', __name__) diff --git a/app/api/users.py b/app/api/users.py index c9ea5d39..11c71c97 100644 --- a/app/api/users.py +++ b/app/api/users.py @@ -3,11 +3,11 @@ from apifairy import authenticate, body, response from apifairy.decorators import other_responses from flask import abort, Blueprint from werkzeug.exceptions import InternalServerError -from app import db from app.email import create_message, send +from app import db from app.models import User -from .schemas import EmptySchema, UserSchema from .auth import auth_error_responses, token_auth +from .schemas import EmptySchema, UserSchema bp = Blueprint('users', __name__) diff --git a/app/contributions/spacy_nlp_pipeline_models/json_routes.py b/app/contributions/spacy_nlp_pipeline_models/json_routes.py index 8c081ce8..a4bfa2f0 100644 --- a/app/contributions/spacy_nlp_pipeline_models/json_routes.py +++ b/app/contributions/spacy_nlp_pipeline_models/json_routes.py @@ -4,7 +4,7 @@ from threading import Thread from app import db from app.decorators import content_negotiation, permission_required from app.models import SpaCyNLPPipelineModel -from .. import bp +from . import bp @bp.route('/spacy-nlp-pipeline-models/', methods=['DELETE']) diff --git a/app/contributions/spacy_nlp_pipeline_models/routes.py b/app/contributions/spacy_nlp_pipeline_models/routes.py index 53593cc8..3bd67d12 100644 --- a/app/contributions/spacy_nlp_pipeline_models/routes.py +++ b/app/contributions/spacy_nlp_pipeline_models/routes.py @@ -8,9 +8,7 @@ from .forms import ( CreateSpaCyNLPPipelineModelForm, UpdateSpaCyNLPPipelineModelForm ) -from .utils import ( - spacy_nlp_pipeline_model_dlc as spacy_nlp_pipeline_model_dlc -) +from .utils import spacy_nlp_pipeline_model_dlc @bp.route('/spacy-nlp-pipeline-models') diff --git a/app/contributions/tesseract_ocr_pipeline_models/routes.py b/app/contributions/tesseract_ocr_pipeline_models/routes.py index 0f0390aa..9471f53d 100644 --- a/app/contributions/tesseract_ocr_pipeline_models/routes.py +++ b/app/contributions/tesseract_ocr_pipeline_models/routes.py @@ -8,9 +8,7 @@ from .forms import ( CreateTesseractOCRPipelineModelForm, UpdateTesseractOCRPipelineModelForm ) -from .utils import ( - tesseract_ocr_pipeline_model_dlc as tesseract_ocr_pipeline_model_dlc -) +from .utils import tesseract_ocr_pipeline_model_dlc @bp.route('/tesseract-ocr-pipeline-models') diff --git a/app/converters/sandpaper.py b/app/converters/sandpaper.py index 86deb8d0..57ac547e 100644 --- a/app/converters/sandpaper.py +++ b/app/converters/sandpaper.py @@ -1,11 +1,11 @@ -from flask import current_app -from app import db -from app.models import User, Corpus, CorpusFile from datetime import datetime +from flask import current_app from pathlib import Path from typing import Dict, List import json import shutil +from app import db +from app.models import User, Corpus, CorpusFile class SandpaperConverter: diff --git a/app/corpora/files/json_routes.py b/app/corpora/files/json_routes.py index f8d5ddb4..d89f3ed6 100644 --- a/app/corpora/files/json_routes.py +++ b/app/corpora/files/json_routes.py @@ -1,7 +1,7 @@ -from flask import abort, current_app +from flask import current_app from threading import Thread -from app import db from app.decorators import content_negotiation +from app import db from app.models import CorpusFile from ..decorators import corpus_follower_permission_required from . import bp diff --git a/app/corpora/files/utils.py b/app/corpora/files/utils.py index 2bb10285..69a66f79 100644 --- a/app/corpora/files/utils.py +++ b/app/corpora/files/utils.py @@ -1,6 +1,5 @@ from flask import request, url_for from app.models import CorpusFile -from ..utils import corpus_endpoint_arguments_constructor as corpus_eac def corpus_file_dynamic_list_constructor(): diff --git a/app/ext/flask_sqlalchemy/__init__.py b/app/ext/flask_sqlalchemy/__init__.py deleted file mode 100644 index fcd46133..00000000 --- a/app/ext/flask_sqlalchemy/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .container_column import ContainerColumn -from .int_enum_column import IntEnumColumn diff --git a/app/ext/flask_sqlalchemy/container_column.py b/app/ext/flask_sqlalchemy/container_column.py deleted file mode 100644 index a93149fc..00000000 --- a/app/ext/flask_sqlalchemy/container_column.py +++ /dev/null @@ -1,21 +0,0 @@ -import json -from app import db - - -class ContainerColumn(db.TypeDecorator): - impl = db.String - - def __init__(self, container_type, *args, **kwargs): - super().__init__(*args, **kwargs) - self.container_type = container_type - - def process_bind_param(self, value, dialect): - if isinstance(value, self.container_type): - return json.dumps(value) - elif isinstance(value, str) and isinstance(json.loads(value), self.container_type): - return value - else: - return TypeError() - - def process_result_value(self, value, dialect): - return json.loads(value) diff --git a/app/extensions/__init__.py b/app/extensions/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/app/extensions/__init__.py @@ -0,0 +1 @@ + diff --git a/app/extensions/sqlalchemy/__init__.py b/app/extensions/sqlalchemy/__init__.py new file mode 100644 index 00000000..47f029db --- /dev/null +++ b/app/extensions/sqlalchemy/__init__.py @@ -0,0 +1,2 @@ +from .types import ContainerColumn +from .types import IntEnumColumn diff --git a/app/ext/flask_sqlalchemy/int_enum_column.py b/app/extensions/sqlalchemy/types.py similarity index 52% rename from app/ext/flask_sqlalchemy/int_enum_column.py rename to app/extensions/sqlalchemy/types.py index a0ba4974..215b19cc 100644 --- a/app/ext/flask_sqlalchemy/int_enum_column.py +++ b/app/extensions/sqlalchemy/types.py @@ -1,6 +1,26 @@ +import json from app import db +class ContainerColumn(db.TypeDecorator): + impl = db.String + + def __init__(self, container_type, *args, **kwargs): + super().__init__(*args, **kwargs) + self.container_type = container_type + + def process_bind_param(self, value, dialect): + if isinstance(value, self.container_type): + return json.dumps(value) + elif isinstance(value, str) and isinstance(json.loads(value), self.container_type): + return value + else: + return TypeError() + + def process_result_value(self, value, dialect): + return json.loads(value) + + class IntEnumColumn(db.TypeDecorator): impl = db.Integer diff --git a/app/wtforms/__init__.py b/app/extensions/wtforms/__init__.py similarity index 100% rename from app/wtforms/__init__.py rename to app/extensions/wtforms/__init__.py diff --git a/app/wtforms/validators.py b/app/extensions/wtforms/validators.py similarity index 100% rename from app/wtforms/validators.py rename to app/extensions/wtforms/validators.py diff --git a/app/models/corpus.py b/app/models/corpus.py index 1d541413..147fbb02 100644 --- a/app/models/corpus.py +++ b/app/models/corpus.py @@ -9,7 +9,7 @@ import shutil import xml.etree.ElementTree as ET from app import db from app.converters.vrt import normalize_vrt_file -from app.ext.flask_sqlalchemy import IntEnumColumn +from app.extensions.sqlalchemy import IntEnumColumn from .corpus_follower_association import CorpusFollowerAssociation diff --git a/app/models/job.py b/app/models/job.py index daa043c5..bba8ea0e 100644 --- a/app/models/job.py +++ b/app/models/job.py @@ -7,7 +7,7 @@ from typing import Union from pathlib import Path import shutil from app import db -from app.ext.flask_sqlalchemy import ContainerColumn, IntEnumColumn +from app.extensions.sqlalchemy import ContainerColumn, IntEnumColumn class JobStatus(IntEnum): diff --git a/app/models/spacy_nlp_pipeline_model.py b/app/models/spacy_nlp_pipeline_model.py index 4cea0d3f..e8a2501b 100644 --- a/app/models/spacy_nlp_pipeline_model.py +++ b/app/models/spacy_nlp_pipeline_model.py @@ -5,7 +5,7 @@ from pathlib import Path import requests import yaml from app import db -from app.ext.flask_sqlalchemy import ContainerColumn +from app.extensions.sqlalchemy import ContainerColumn from .file_mixin import FileMixin from .user import User diff --git a/app/models/tesseract_ocr_pipeline_model.py b/app/models/tesseract_ocr_pipeline_model.py index 20f5feee..43198711 100644 --- a/app/models/tesseract_ocr_pipeline_model.py +++ b/app/models/tesseract_ocr_pipeline_model.py @@ -5,7 +5,7 @@ from pathlib import Path import requests import yaml from app import db -from app.ext.flask_sqlalchemy import ContainerColumn +from app.extensions.sqlalchemy import ContainerColumn from .file_mixin import FileMixin from .user import User diff --git a/app/models/user.py b/app/models/user.py index 8ba96b14..829337ca 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -12,7 +12,7 @@ import re import secrets import shutil from app import db, hashids -from app.ext.flask_sqlalchemy import IntEnumColumn +from app.extensions.sqlalchemy import IntEnumColumn from .corpus import Corpus from .corpus_follower_association import CorpusFollowerAssociation from .corpus_follower_role import CorpusFollowerRole diff --git a/app/static/js/forms/base-form-new.js b/app/static/js/forms/base-form-new.js new file mode 100644 index 00000000..6689eee5 --- /dev/null +++ b/app/static/js/forms/base-form-new.js @@ -0,0 +1,57 @@ +export class BaseForm { + constructor(formElement) { + this.element = formElement; + + this.element.addEventListener('submit', (event) => { + event.preventDefault(); + this.submit(); + }); + console.log("UsernamePostFormInitialized"); + } + + submit() { + let errorTextElements = this.element + .querySelectorAll('.supporting-text[data-supporting-text-type="error"]'); + for (let errorTextElement of errorTextElements) {errorTextElement.remove();} + + const body = new FormData(this.element); + const headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }; + const method = this.element.method; + + const fetchPromise = new Promise((resolve, reject) => { + fetch(this.element.action, {body: body, headers: headers, method: method}) + .then((response) => { + if (!response.ok) { + console.log("reject", response); + reject(response); + return; + } + console.log("resolve", response); + resolve(response); + }); + }); + + fetchPromise + .then( + (response) => {console.log("Hello from resolve handler");return response.json();}, + (response) => { + console.log("Hello from reject handler 1/2"); + response.json() + .then((errors) => { + console.log("Hello from reject handler 2/2"); + for (let [name, messages] of Object.entries(errors)) { + console.log(name, messages); + const inputFieldElement = this.element[name].closest('.input-field'); + for (let message of messages) { + const messageHTML = `${message}`; + inputFieldElement.insertAdjacentHTML('beforeend', messageHTML); + } + } + }); + } + ); + } +}; diff --git a/app/users/settings/forms.py b/app/users/settings/forms.py index 71c29456..dc4687c1 100644 --- a/app/users/settings/forms.py +++ b/app/users/settings/forms.py @@ -1,4 +1,3 @@ -from flask_login import current_user from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileRequired from wtforms import ( @@ -17,7 +16,7 @@ from wtforms.validators import ( Regexp ) from app.models import User, UserSettingJobStatusMailNotificationLevel -from app.wtforms.validators import FileSize +from app.extensions.wtforms.validators import FileSize class UpdateAccountInformationForm(FlaskForm): diff --git a/requirements.txt b/requirements.txt index 9002ce02..422d1a2d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ Flask-Marshmallow==0.14.0 Flask-Menu==0.7.2 Flask-Migrate Flask-Paranoid -Flask-SocketIO +Flask-SocketIO==5.3.6 Flask-SQLAlchemy==2.5.1 Flask-WTF hiredis