Compare commits

..

No commits in common. "cdf6f9fcfdd268c25cd8fd156368402a077721ca" and "82d6f6003f8a21d1d3b13829bfe809a0c8a97e78" have entirely different histories.

35 changed files with 64 additions and 108 deletions

View File

@ -5,8 +5,9 @@
!app !app
!migrations !migrations
!tests !tests
!.flaskenv
!boot.sh !boot.sh
!config.py !config.py
!docker-nopaque-entrypoint.sh !docker-nopaque-entrypoint.sh
!nopaque.py
!requirements.txt !requirements.txt
!wsgi.py

1
.flaskenv Normal file
View File

@ -0,0 +1 @@
FLASK_APP=nopaque.py

View File

@ -46,7 +46,7 @@ COPY docker-nopaque-entrypoint.sh /usr/local/bin/
COPY --chown=nopaque:nopaque app app COPY --chown=nopaque:nopaque app app
COPY --chown=nopaque:nopaque migrations migrations COPY --chown=nopaque:nopaque migrations migrations
COPY --chown=nopaque:nopaque tests tests COPY --chown=nopaque:nopaque tests tests
COPY --chown=nopaque:nopaque boot.sh config.py wsgi.py requirements.txt ./ COPY --chown=nopaque:nopaque .flaskenv boot.sh config.py nopaque.py requirements.txt ./
RUN mkdir logs RUN mkdir logs

View File

@ -33,9 +33,6 @@ scheduler = APScheduler()
socketio = SocketIO() socketio = SocketIO()
# TODO: Create export for lemmatized corpora
def create_app(config: Config = Config) -> Flask: def create_app(config: Config = Config) -> Flask:
''' Creates an initialized Flask (WSGI Application) object. ''' ''' Creates an initialized Flask (WSGI Application) object. '''
app = Flask(__name__) app = Flask(__name__)

View File

@ -1,6 +1,6 @@
from flask import abort, request from flask import abort, request
from app.decorators import content_negotiation
from app import db from app import db
from app.decorators import content_negotiation
from app.models import User from app.models import User
from . import bp from . import bp

View File

@ -9,12 +9,12 @@ from app.users.settings.forms import (
UpdateAccountInformationForm, UpdateAccountInformationForm,
UpdateProfileInformationForm UpdateProfileInformationForm
) )
from . import bp
from .forms import UpdateUserForm
from app.users.utils import ( from app.users.utils import (
user_endpoint_arguments_constructor as user_eac, user_endpoint_arguments_constructor as user_eac,
user_dynamic_list_constructor as user_dlc user_dynamic_list_constructor as user_dlc
) )
from . import bp
from .forms import UpdateUserForm
@bp.route('') @bp.route('')

View File

@ -5,8 +5,8 @@ from flask import abort, Blueprint
from werkzeug.exceptions import InternalServerError from werkzeug.exceptions import InternalServerError
from app import db, hashids from app import db, hashids
from app.models import Job, JobInput, JobStatus, TesseractOCRPipelineModel from app.models import Job, JobInput, JobStatus, TesseractOCRPipelineModel
from .auth import auth_error_responses, token_auth
from .schemas import EmptySchema, JobSchema, SpaCyNLPPipelineJobSchema, TesseractOCRPipelineJobSchema, TesseractOCRPipelineModelSchema from .schemas import EmptySchema, JobSchema, SpaCyNLPPipelineJobSchema, TesseractOCRPipelineJobSchema, TesseractOCRPipelineModelSchema
from .auth import auth_error_responses, token_auth
bp = Blueprint('jobs', __name__) bp = Blueprint('jobs', __name__)

View File

@ -3,11 +3,11 @@ from apifairy import authenticate, body, response
from apifairy.decorators import other_responses from apifairy.decorators import other_responses
from flask import abort, Blueprint from flask import abort, Blueprint
from werkzeug.exceptions import InternalServerError from werkzeug.exceptions import InternalServerError
from app.email import create_message, send
from app import db from app import db
from app.email import create_message, send
from app.models import User from app.models import User
from .auth import auth_error_responses, token_auth
from .schemas import EmptySchema, UserSchema from .schemas import EmptySchema, UserSchema
from .auth import auth_error_responses, token_auth
bp = Blueprint('users', __name__) bp = Blueprint('users', __name__)

View File

@ -4,7 +4,7 @@ from threading import Thread
from app import db from app import db
from app.decorators import content_negotiation, permission_required from app.decorators import content_negotiation, permission_required
from app.models import SpaCyNLPPipelineModel from app.models import SpaCyNLPPipelineModel
from . import bp from .. import bp
@bp.route('/spacy-nlp-pipeline-models/<hashid:spacy_nlp_pipeline_model_id>', methods=['DELETE']) @bp.route('/spacy-nlp-pipeline-models/<hashid:spacy_nlp_pipeline_model_id>', methods=['DELETE'])

View File

@ -8,7 +8,9 @@ from .forms import (
CreateSpaCyNLPPipelineModelForm, CreateSpaCyNLPPipelineModelForm,
UpdateSpaCyNLPPipelineModelForm UpdateSpaCyNLPPipelineModelForm
) )
from .utils import spacy_nlp_pipeline_model_dlc from .utils import (
spacy_nlp_pipeline_model_dlc as spacy_nlp_pipeline_model_dlc
)
@bp.route('/spacy-nlp-pipeline-models') @bp.route('/spacy-nlp-pipeline-models')

View File

@ -8,7 +8,9 @@ from .forms import (
CreateTesseractOCRPipelineModelForm, CreateTesseractOCRPipelineModelForm,
UpdateTesseractOCRPipelineModelForm UpdateTesseractOCRPipelineModelForm
) )
from .utils import tesseract_ocr_pipeline_model_dlc from .utils import (
tesseract_ocr_pipeline_model_dlc as tesseract_ocr_pipeline_model_dlc
)
@bp.route('/tesseract-ocr-pipeline-models') @bp.route('/tesseract-ocr-pipeline-models')

View File

@ -1,11 +1,11 @@
from datetime import datetime
from flask import current_app from flask import current_app
from app import db
from app.models import User, Corpus, CorpusFile
from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Dict, List from typing import Dict, List
import json import json
import shutil import shutil
from app import db
from app.models import User, Corpus, CorpusFile
class SandpaperConverter: class SandpaperConverter:

View File

@ -1,7 +1,7 @@
from flask import current_app from flask import abort, current_app
from threading import Thread from threading import Thread
from app.decorators import content_negotiation
from app import db from app import db
from app.decorators import content_negotiation
from app.models import CorpusFile from app.models import CorpusFile
from ..decorators import corpus_follower_permission_required from ..decorators import corpus_follower_permission_required
from . import bp from . import bp

View File

@ -1,5 +1,6 @@
from flask import request, url_for from flask import request, url_for
from app.models import CorpusFile from app.models import CorpusFile
from ..utils import corpus_endpoint_arguments_constructor as corpus_eac
def corpus_file_dynamic_list_constructor(): def corpus_file_dynamic_list_constructor():

View File

@ -0,0 +1,2 @@
from .container_column import ContainerColumn
from .int_enum_column import IntEnumColumn

View File

@ -0,0 +1,21 @@
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)

View File

@ -1,26 +1,6 @@
import json
from app import db 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): class IntEnumColumn(db.TypeDecorator):
impl = db.Integer impl = db.Integer

View File

@ -1 +0,0 @@

View File

@ -1,2 +0,0 @@
from .types import ContainerColumn
from .types import IntEnumColumn

View File

@ -9,7 +9,7 @@ import shutil
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from app import db from app import db
from app.converters.vrt import normalize_vrt_file from app.converters.vrt import normalize_vrt_file
from app.extensions.sqlalchemy import IntEnumColumn from app.ext.flask_sqlalchemy import IntEnumColumn
from .corpus_follower_association import CorpusFollowerAssociation from .corpus_follower_association import CorpusFollowerAssociation

View File

@ -7,7 +7,7 @@ from typing import Union
from pathlib import Path from pathlib import Path
import shutil import shutil
from app import db from app import db
from app.extensions.sqlalchemy import ContainerColumn, IntEnumColumn from app.ext.flask_sqlalchemy import ContainerColumn, IntEnumColumn
class JobStatus(IntEnum): class JobStatus(IntEnum):

View File

@ -5,7 +5,7 @@ from pathlib import Path
import requests import requests
import yaml import yaml
from app import db from app import db
from app.extensions.sqlalchemy import ContainerColumn from app.ext.flask_sqlalchemy import ContainerColumn
from .file_mixin import FileMixin from .file_mixin import FileMixin
from .user import User from .user import User

View File

@ -5,7 +5,7 @@ from pathlib import Path
import requests import requests
import yaml import yaml
from app import db from app import db
from app.extensions.sqlalchemy import ContainerColumn from app.ext.flask_sqlalchemy import ContainerColumn
from .file_mixin import FileMixin from .file_mixin import FileMixin
from .user import User from .user import User

View File

@ -12,7 +12,7 @@ import re
import secrets import secrets
import shutil import shutil
from app import db, hashids from app import db, hashids
from app.extensions.sqlalchemy import IntEnumColumn from app.ext.flask_sqlalchemy import IntEnumColumn
from .corpus import Corpus from .corpus import Corpus
from .corpus_follower_association import CorpusFollowerAssociation from .corpus_follower_association import CorpusFollowerAssociation
from .corpus_follower_role import CorpusFollowerRole from .corpus_follower_role import CorpusFollowerRole

View File

@ -1,57 +0,0 @@
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 = `<span class="supporting-text" data-supporting-text-type="error">${message}</span>`;
inputFieldElement.insertAdjacentHTML('beforeend', messageHTML);
}
}
});
}
);
}
};

View File

@ -1,3 +1,4 @@
from flask_login import current_user
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 ( from wtforms import (
@ -16,7 +17,7 @@ from wtforms.validators import (
Regexp Regexp
) )
from app.models import User, UserSettingJobStatusMailNotificationLevel from app.models import User, UserSettingJobStatusMailNotificationLevel
from app.extensions.wtforms.validators import FileSize from app.wtforms.validators import FileSize
class UpdateAccountInformationForm(FlaskForm): class UpdateAccountInformationForm(FlaskForm):

View File

@ -24,7 +24,7 @@ if [[ "${#}" == "0" ]]; then
sleep 5 sleep 5
done done
fi fi
python3 wsgi.py python3 nopaque.py
elif [[ "${1}" == "flask" ]]; then elif [[ "${1}" == "flask" ]]; then
flask ${@:2} flask ${@:2}
elif [[ "${1}" == "--help" || "${1}" == "-h" ]]; then elif [[ "${1}" == "--help" || "${1}" == "-h" ]]; then

View File

@ -1,3 +1,5 @@
version: "3.5"
# The docker-compose.yml file is not meant to be modified itself. # The docker-compose.yml file is not meant to be modified itself.
# Instead use the following files for configurations: # Instead use the following files for configurations:
# - .env: Environment variables for the docker-compose.yml file. # - .env: Environment variables for the docker-compose.yml file.

View File

@ -1,3 +1,5 @@
version: "3.5"
services: services:
nopaque: nopaque:
environment: environment:
@ -11,6 +13,6 @@ services:
- "./config.py:/home/nopaque/config.py" - "./config.py:/home/nopaque/config.py"
- "./docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh" - "./docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh"
- "./migrations:/home/nopaque/migrations" - "./migrations:/home/nopaque/migrations"
- "./nopaque.py:/home/nopaque/nopaque.py"
- "./requirements.txt:/home/nopaque/requirements.txt" - "./requirements.txt:/home/nopaque/requirements.txt"
- "./tests:/home/nopaque/tests" - "./tests:/home/nopaque/tests"
- "./wsgi.py:/home/nopaque/wsgi.py"

View File

@ -1,3 +1,5 @@
version: "3.5"
services: services:
nopaque: nopaque:
environment: environment:

View File

@ -1,3 +1,5 @@
version: "3.5"
networks: networks:
traefik: traefik:
external: true external: true

View File

View File

@ -15,7 +15,7 @@ Flask-Marshmallow==0.14.0
Flask-Menu==0.7.2 Flask-Menu==0.7.2
Flask-Migrate Flask-Migrate
Flask-Paranoid Flask-Paranoid
Flask-SocketIO==5.3.6 Flask-SocketIO
Flask-SQLAlchemy==2.5.1 Flask-SQLAlchemy==2.5.1
Flask-WTF Flask-WTF
hiredis hiredis