mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-12-27 03:44:19 +00:00
Compare commits
No commits in common. "02e6c7c16c980d1231b2b851f026ed398416ac16" and "81c6f32a354a1dee9be5f4933d0f6b69951c8776" have entirely different histories.
02e6c7c16c
...
81c6f32a35
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,6 +2,8 @@
|
|||||||
app/static/gen/
|
app/static/gen/
|
||||||
volumes/
|
volumes/
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
|
logs/
|
||||||
|
!logs/dummy
|
||||||
*.env
|
*.env
|
||||||
|
|
||||||
*.pjentsch-testing
|
*.pjentsch-testing
|
||||||
|
@ -46,6 +46,7 @@ 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 ./
|
COPY --chown=nopaque:nopaque boot.sh config.py wsgi.py ./
|
||||||
|
RUN mkdir logs
|
||||||
|
|
||||||
|
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
@ -35,7 +35,7 @@ username@hostname:~$ sudo mount --types cifs --options gid=${USER},password=nopa
|
|||||||
# Clone the nopaque repository
|
# Clone the nopaque repository
|
||||||
username@hostname:~$ git clone https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
|
username@hostname:~$ git clone https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
|
||||||
# Create data directories
|
# Create data directories
|
||||||
username@hostname:~$ mkdir volumes/{db,mq}
|
username@hostname:~$ mkdir data/{db,logs,mq}
|
||||||
username@hostname:~$ cp db.env.tpl db.env
|
username@hostname:~$ cp db.env.tpl db.env
|
||||||
username@hostname:~$ cp .env.tpl .env
|
username@hostname:~$ cp .env.tpl .env
|
||||||
# Fill out the variables within these files.
|
# Fill out the variables within these files.
|
||||||
|
142
app/__init__.py
142
app/__init__.py
@ -14,11 +14,10 @@ from flask_sqlalchemy import SQLAlchemy
|
|||||||
from flask_hashids import Hashids
|
from flask_hashids import Hashids
|
||||||
|
|
||||||
|
|
||||||
docker_client = DockerClient.from_env()
|
|
||||||
|
|
||||||
apifairy = APIFairy()
|
apifairy = APIFairy()
|
||||||
assets = Environment()
|
assets = Environment()
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
|
docker_client = DockerClient()
|
||||||
hashids = Hashids()
|
hashids = Hashids()
|
||||||
login = LoginManager()
|
login = LoginManager()
|
||||||
ma = Marshmallow()
|
ma = Marshmallow()
|
||||||
@ -29,129 +28,80 @@ scheduler = APScheduler()
|
|||||||
socketio = SocketIO()
|
socketio = SocketIO()
|
||||||
|
|
||||||
|
|
||||||
def create_app(config: Config = Config) -> Flask:
|
# TODO: Create export for lemmatized corpora
|
||||||
''' Creates an initialized Flask object. '''
|
|
||||||
|
|
||||||
|
|
||||||
|
def create_app(config: Config = Config) -> Flask:
|
||||||
|
''' Creates an initialized Flask (WSGI Application) object. '''
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_object(config)
|
app.config.from_object(config)
|
||||||
|
config.init_app(app)
|
||||||
_configure_logging(app)
|
|
||||||
_configure_middlewares(app)
|
|
||||||
_init_docker_client(app)
|
|
||||||
_init_extensions(app)
|
|
||||||
_register_blueprints(app)
|
|
||||||
_register_socketio_namespaces(app)
|
|
||||||
_register_db_event_listeners(app)
|
|
||||||
|
|
||||||
return app
|
|
||||||
|
|
||||||
|
|
||||||
def _configure_logging(app: Flask):
|
|
||||||
from flask.logging import default_handler
|
|
||||||
from logging import Formatter, StreamHandler
|
|
||||||
|
|
||||||
log_date_format: str = app.config['NOPAQUE_LOG_DATE_FORMAT']
|
|
||||||
log_format: str = app.config['NOPAQUE_LOG_FORMAT']
|
|
||||||
log_level: str = app.config['NOPAQUE_LOG_LEVEL']
|
|
||||||
|
|
||||||
formatter = Formatter(fmt=log_format, datefmt=log_date_format)
|
|
||||||
handler = StreamHandler()
|
|
||||||
handler.setFormatter(formatter)
|
|
||||||
handler.setLevel(log_level)
|
|
||||||
|
|
||||||
app.logger.removeHandler(default_handler)
|
|
||||||
app.logger.addHandler(handler)
|
|
||||||
|
|
||||||
|
|
||||||
def _configure_middlewares(app: Flask):
|
|
||||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
|
||||||
|
|
||||||
if app.config['NOPAQUE_PROXY_FIX_ENABLED']:
|
|
||||||
app.wsgi_app = ProxyFix(
|
|
||||||
app.wsgi_app,
|
|
||||||
x_for=app.config['NOPAQUE_PROXY_FIX_X_FOR'],
|
|
||||||
x_host=app.config['NOPAQUE_PROXY_FIX_X_HOST'],
|
|
||||||
x_port=app.config['NOPAQUE_PROXY_FIX_X_PORT'],
|
|
||||||
x_prefix=app.config['NOPAQUE_PROXY_FIX_X_PREFIX'],
|
|
||||||
x_proto=app.config['NOPAQUE_PROXY_FIX_X_PROTO']
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _init_docker_client(app: Flask):
|
|
||||||
registry: str = app.config['NOPAQUE_DOCKER_REGISTRY']
|
|
||||||
username: str = app.config['NOPAQUE_DOCKER_REGISTRY_USERNAME']
|
|
||||||
password: str = app.config['NOPAQUE_DOCKER_REGISTRY_PASSWORD']
|
|
||||||
|
|
||||||
docker_client.login(
|
docker_client.login(
|
||||||
username,
|
app.config['NOPAQUE_DOCKER_REGISTRY_USERNAME'],
|
||||||
password=password,
|
password=app.config['NOPAQUE_DOCKER_REGISTRY_PASSWORD'],
|
||||||
registry=registry
|
registry=app.config['NOPAQUE_DOCKER_REGISTRY']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _init_extensions(app: Flask):
|
|
||||||
from typing import Callable
|
|
||||||
from .daemon import daemon
|
|
||||||
from .models import AnonymousUser, User
|
|
||||||
|
|
||||||
is_primary_instance: bool = app.config['NOPAQUE_IS_PRIMARY_INSTANCE']
|
|
||||||
socketio_message_queue_uri: str = app.config['NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI']
|
|
||||||
login_user_loader_callback: Callable[[int], User | None] = lambda user_id: User.query.get(int(user_id))
|
|
||||||
|
|
||||||
apifairy.init_app(app)
|
apifairy.init_app(app)
|
||||||
assets.init_app(app)
|
assets.init_app(app)
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
hashids.init_app(app)
|
hashids.init_app(app)
|
||||||
login.init_app(app)
|
login.init_app(app)
|
||||||
login.anonymous_user = AnonymousUser
|
|
||||||
login.login_view = 'auth.login'
|
|
||||||
login.user_loader(login_user_loader_callback)
|
|
||||||
ma.init_app(app)
|
ma.init_app(app)
|
||||||
mail.init_app(app)
|
mail.init_app(app)
|
||||||
migrate.init_app(app, db)
|
migrate.init_app(app, db)
|
||||||
paranoid.init_app(app)
|
paranoid.init_app(app)
|
||||||
paranoid.redirect_view = '/'
|
|
||||||
scheduler.init_app(app)
|
scheduler.init_app(app)
|
||||||
if is_primary_instance:
|
socketio.init_app(app, message_queue=app.config['NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI']) # noqa
|
||||||
scheduler.add_job('daemon', daemon, args=(app,), seconds=3, trigger='interval')
|
|
||||||
socketio.init_app(app, message_queue=socketio_message_queue_uri)
|
|
||||||
|
|
||||||
|
from .models import AnonymousUser, User
|
||||||
|
login.anonymous_user = AnonymousUser
|
||||||
|
login.login_view = 'auth.login'
|
||||||
|
@login.user_loader
|
||||||
|
def load_user(user_id):
|
||||||
|
return User.query.get(int(user_id))
|
||||||
|
|
||||||
|
paranoid.redirect_view = '/'
|
||||||
|
|
||||||
|
from .models.event_listeners import register_event_listeners
|
||||||
|
register_event_listeners()
|
||||||
|
|
||||||
def _register_blueprints(app: Flask):
|
|
||||||
from .admin import bp as admin_blueprint
|
from .admin import bp as admin_blueprint
|
||||||
from .api import bp as api_blueprint
|
|
||||||
from .auth import bp as auth_blueprint
|
|
||||||
from .contributions import bp as contributions_blueprint
|
|
||||||
from .corpora import bp as corpora_blueprint
|
|
||||||
from .errors import bp as errors_bp
|
|
||||||
from .jobs import bp as jobs_blueprint
|
|
||||||
from .main import bp as main_blueprint
|
|
||||||
from .services import bp as services_blueprint
|
|
||||||
from .settings import bp as settings_blueprint
|
|
||||||
from .users import bp as users_blueprint
|
|
||||||
from .workshops import bp as workshops_blueprint
|
|
||||||
|
|
||||||
app.register_blueprint(admin_blueprint, url_prefix='/admin')
|
app.register_blueprint(admin_blueprint, url_prefix='/admin')
|
||||||
|
|
||||||
|
from .api import bp as api_blueprint
|
||||||
app.register_blueprint(api_blueprint, url_prefix='/api')
|
app.register_blueprint(api_blueprint, url_prefix='/api')
|
||||||
|
|
||||||
|
from .auth import bp as auth_blueprint
|
||||||
app.register_blueprint(auth_blueprint)
|
app.register_blueprint(auth_blueprint)
|
||||||
|
|
||||||
|
from .contributions import bp as contributions_blueprint
|
||||||
app.register_blueprint(contributions_blueprint, url_prefix='/contributions')
|
app.register_blueprint(contributions_blueprint, url_prefix='/contributions')
|
||||||
|
|
||||||
|
from .corpora import bp as corpora_blueprint
|
||||||
|
from .corpora.cqi_over_sio import CQiNamespace
|
||||||
app.register_blueprint(corpora_blueprint, cli_group='corpus', url_prefix='/corpora')
|
app.register_blueprint(corpora_blueprint, cli_group='corpus', url_prefix='/corpora')
|
||||||
|
socketio.on_namespace(CQiNamespace('/cqi_over_sio'))
|
||||||
|
|
||||||
|
from .errors import bp as errors_bp
|
||||||
app.register_blueprint(errors_bp)
|
app.register_blueprint(errors_bp)
|
||||||
|
|
||||||
|
from .jobs import bp as jobs_blueprint
|
||||||
app.register_blueprint(jobs_blueprint, url_prefix='/jobs')
|
app.register_blueprint(jobs_blueprint, url_prefix='/jobs')
|
||||||
|
|
||||||
|
from .main import bp as main_blueprint
|
||||||
app.register_blueprint(main_blueprint, cli_group=None)
|
app.register_blueprint(main_blueprint, cli_group=None)
|
||||||
|
|
||||||
|
from .services import bp as services_blueprint
|
||||||
app.register_blueprint(services_blueprint, url_prefix='/services')
|
app.register_blueprint(services_blueprint, url_prefix='/services')
|
||||||
|
|
||||||
|
from .settings import bp as settings_blueprint
|
||||||
app.register_blueprint(settings_blueprint, url_prefix='/settings')
|
app.register_blueprint(settings_blueprint, url_prefix='/settings')
|
||||||
|
|
||||||
|
from .users import bp as users_blueprint
|
||||||
app.register_blueprint(users_blueprint, cli_group='user', url_prefix='/users')
|
app.register_blueprint(users_blueprint, cli_group='user', url_prefix='/users')
|
||||||
|
|
||||||
|
from .workshops import bp as workshops_blueprint
|
||||||
app.register_blueprint(workshops_blueprint, url_prefix='/workshops')
|
app.register_blueprint(workshops_blueprint, url_prefix='/workshops')
|
||||||
|
|
||||||
|
return app
|
||||||
def _register_socketio_namespaces(app: Flask):
|
|
||||||
from .corpora.cqi_over_sio import CQiOverSocketIO
|
|
||||||
|
|
||||||
socketio.on_namespace(CQiOverSocketIO('/cqi_over_sio'))
|
|
||||||
|
|
||||||
|
|
||||||
def _register_db_event_listeners(app: Flask):
|
|
||||||
from .models.event_listeners import register_event_listeners
|
|
||||||
|
|
||||||
register_event_listeners()
|
|
||||||
|
@ -19,7 +19,7 @@ This package tunnels the Corpus Query interface (CQi) protocol through
|
|||||||
Socket.IO (SIO) by tunneling CQi API calls through an event called "exec".
|
Socket.IO (SIO) by tunneling CQi API calls through an event called "exec".
|
||||||
|
|
||||||
Basic concept:
|
Basic concept:
|
||||||
1. A client connects to the namespace.
|
1. A client connects to the "/cqi_over_sio" namespace.
|
||||||
2. The client emits the "init" event and provides a corpus id for the corpus
|
2. The client emits the "init" event and provides a corpus id for the corpus
|
||||||
that should be analysed in this session.
|
that should be analysed in this session.
|
||||||
1.1 The analysis session counter of the corpus is incremented.
|
1.1 The analysis session counter of the corpus is incremented.
|
||||||
@ -28,13 +28,14 @@ Basic concept:
|
|||||||
1.4 Connect the CQiClient to the server.
|
1.4 Connect the CQiClient to the server.
|
||||||
1.5 Save the CQiClient, the Lock and the corpus id in the session for
|
1.5 Save the CQiClient, the Lock and the corpus id in the session for
|
||||||
subsequential use.
|
subsequential use.
|
||||||
3. The client emits "exec" events, within which it provides the name of a CQi
|
2. The client emits the "exec" event provides the name of a CQi API function
|
||||||
API function and the corresponding arguments.
|
arguments (optional).
|
||||||
3.1 The "exec" event handler will execute the function, make sure that
|
- The event "exec" handler will execute the function, make sure that the
|
||||||
the result is serializable and returns the result back to the client.
|
result is serializable and returns the result back to the client.
|
||||||
4. The client disconnects from the namespace
|
4. Wait for more events
|
||||||
4.1 The analysis session counter of the corpus is decremented.
|
5. The client disconnects from the "/cqi_over_sio" namespace
|
||||||
4.2 The CQiClient and (Mutex) Lock belonging to it are teared down.
|
1.1 The analysis session counter of the corpus is decremented.
|
||||||
|
1.2 The CQiClient and (Mutex) Lock belonging to it are teared down.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
CQI_API_FUNCTION_NAMES: List[str] = [
|
CQI_API_FUNCTION_NAMES: List[str] = [
|
||||||
@ -85,7 +86,7 @@ CQI_API_FUNCTION_NAMES: List[str] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CQiOverSocketIO(Namespace):
|
class CQiNamespace(Namespace):
|
||||||
@socketio_login_required
|
@socketio_login_required
|
||||||
def on_connect(self):
|
def on_connect(self):
|
||||||
pass
|
pass
|
||||||
|
@ -14,7 +14,7 @@ from .forms import CreateCorpusForm
|
|||||||
|
|
||||||
@bp.route('')
|
@bp.route('')
|
||||||
def corpora():
|
def corpora():
|
||||||
return redirect(url_for('main.dashboard', _anchor='corpora'))
|
return render_template('corpora/corpora.html.j2', title='Corpora')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/create', methods=['GET', 'POST'])
|
@bp.route('/create', methods=['GET', 'POST'])
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from flask import Flask
|
|
||||||
from app import db
|
from app import db
|
||||||
|
from flask import Flask
|
||||||
from .corpus_utils import check_corpora
|
from .corpus_utils import check_corpora
|
||||||
from .job_utils import check_jobs
|
from .job_utils import check_jobs
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ from . import bp
|
|||||||
|
|
||||||
@bp.route('')
|
@bp.route('')
|
||||||
def jobs():
|
def jobs():
|
||||||
return redirect(url_for('main.dashboard', _anchor='jobs'))
|
# return redirect(url_for('main.dashboard', _anchor='jobs'))
|
||||||
|
return render_template('jobs/jobs.html.j2', title='Jobs')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<hashid:job_id>')
|
@bp.route('/<hashid:job_id>')
|
||||||
|
@ -12,7 +12,7 @@ body[data-sidenav-fixed="true" i] main,
|
|||||||
body[data-sidenav-fixed="true" i] footer {
|
body[data-sidenav-fixed="true" i] footer {
|
||||||
padding-left: 300px;
|
padding-left: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width : 992px) {
|
@media only screen and (max-width : 992px) {
|
||||||
body[data-sidenav-fixed="true" i] header,
|
body[data-sidenav-fixed="true" i] header,
|
||||||
body[data-sidenav-fixed="true" i] main,
|
body[data-sidenav-fixed="true" i] main,
|
||||||
@ -34,7 +34,7 @@ body[data-sidenav-fixed="true" i] .navbar-fixed > nav {
|
|||||||
|
|
||||||
/* #region sticky-footer */
|
/* #region sticky-footer */
|
||||||
/*
|
/*
|
||||||
* Sticky Footer:
|
* Sticky Footer:
|
||||||
* A sticky footer always stays on the bottom of the page regardless of how
|
* A sticky footer always stays on the bottom of the page regardless of how
|
||||||
* little content is on the page. However, this footer will be pushed down if
|
* little content is on the page. However, this footer will be pushed down if
|
||||||
* there is a lot of content, so it is different from a fixed footer.
|
* there is a lot of content, so it is different from a fixed footer.
|
||||||
|
@ -35,67 +35,3 @@
|
|||||||
/* Support for IE. */
|
/* Support for IE. */
|
||||||
font-feature-settings: 'liga';
|
font-feature-settings: 'liga';
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-nopaque-icon="speedometer"] {
|
|
||||||
--nopaque-icon-character: "A";
|
|
||||||
--nopaque-negative-icon-character: "a";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-nopaque-icon="cloud-processes"] {
|
|
||||||
--nopaque-icon-character: "B";
|
|
||||||
--nopaque-negative-icon-character: "b";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-nopaque-icon="account-circle"] {
|
|
||||||
--nopaque-icon-character: "C";
|
|
||||||
--nopaque-negative-icon-character: "c";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-nopaque-icon="meshed-gears"] {
|
|
||||||
--nopaque-icon-character: "D";
|
|
||||||
--nopaque-negative-icon-character: "d";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-nopaque-icon="file-setup-pipeline"] {
|
|
||||||
--nopaque-icon-character: "E";
|
|
||||||
--nopaque-negative-icon-character: "e";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-nopaque-icon="tesseract-ocr-pipeline"],
|
|
||||||
[data-nopaque-icon="transkribus-htr-pipeline"] {
|
|
||||||
--nopaque-icon-character: "F";
|
|
||||||
--nopaque-negative-icon-character: "f";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-nopaque-icon="spacy-nlp-pipeline"] {
|
|
||||||
--nopaque-icon-character: "G";
|
|
||||||
--nopaque-negative-icon-character: "g";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-nopaque-icon="corpus-analysis"] {
|
|
||||||
--nopaque-icon-character: "H";
|
|
||||||
--nopaque-negative-icon-character: "h";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-nopaque-icon="file-cabinet"] {
|
|
||||||
--nopaque-icon-character: "I";
|
|
||||||
--nopaque-negative-icon-character: "i";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-nopaque-icon="suitcase"] {
|
|
||||||
--nopaque-icon-character: "J";
|
|
||||||
--nopaque-negative-icon-character: "j";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-nopaque-icon="wrench"] {
|
|
||||||
--nopaque-icon-character: "K";
|
|
||||||
--nopaque-negative-icon-character: "k";
|
|
||||||
}
|
|
||||||
|
|
||||||
.nopaque-icons[data-nopaque-icon]:empty::before {
|
|
||||||
content: var(--nopaque-icon-character);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nopaque-icons.negative[data-nopaque-icon]:empty::before{
|
|
||||||
content: var(--nopaque-negative-icon-character);
|
|
||||||
}
|
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
<a class="waves-effect" href="{{ url_for('jobs.jobs') }}"><i class="nopaque-icons">J</i>My Jobs</a>
|
<a class="waves-effect" href="{{ url_for('jobs.jobs') }}"><i class="nopaque-icons">J</i>My Jobs</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="waves-effect" href="{{ url_for('contributions.contributions') }}"><i class="material-icons">new_label</i>My Contributions</a>
|
<a class="waves-effect" href="{{ url_for('main.dashboard', _anchor='contributions') }}"><i class="material-icons">new_label</i>My Contributions</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
{# processes & services items #}
|
{# processes & services items #}
|
||||||
|
38
app/templates/corpora/corpora.html.j2
Normal file
38
app/templates/corpora/corpora.html.j2
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{% extends "base.html.j2" %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block page_content %}
|
||||||
|
<div class="container">
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="corpus-list" data-user-id="{{ current_user.hashid }}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card-action right-align">
|
||||||
|
<a class="btn waves-effect waves-light" href="{{ url_for('corpora.create_corpus') }}">Create corpus<i class="material-icons right">add</i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#
|
||||||
|
<div class="parallax-container">
|
||||||
|
<div class="parallax">
|
||||||
|
<img src="{{ url_for('static', filename='images/parallax_lq/01_books_antique_book_old.jpg') }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div style="position: relative; top: -225px;">
|
||||||
|
<div class="card white">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="corpus-list" data-user-id="{{ current_user.hashid }}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card-action right-align">
|
||||||
|
<a class="btn waves-effect waves-light" href="{{ url_for('corpora.create_corpus') }}">Create corpus<i class="material-icons right">add</i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
#}
|
||||||
|
{% endblock page_content %}
|
98
app/templates/jobs/jobs.html.j2
Normal file
98
app/templates/jobs/jobs.html.j2
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{% extends "base.html.j2" %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block page_content %}
|
||||||
|
<div class="container">
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="job-list" data-user-id="{{ current_user.hashid }}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card-action right-align">
|
||||||
|
<a class="btn modal-trigger waves-effect waves-light" data-target="create-job-modal">Create job<i class="material-icons right">add</i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fixed-action-btn">
|
||||||
|
<a class="btn-floating btn-large">
|
||||||
|
<i class="large material-icons">add</i>
|
||||||
|
</a>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a class="btn-floating service-color darken tooltipped" data-position="left" data-tooltip="File Setup Pipeline" data-service="file-setup-pipeline">
|
||||||
|
<i class="nopaque-icons service-icons" data-service="file-setup-pipeline"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="btn-floating service-color darken tooltipped" data-position="left" data-tooltip="Tesseract OCR Pipeline" data-service="tesseract-ocr-pipeline">
|
||||||
|
<i class="nopaque-icons service-icons" data-service="tesseract-ocr-pipeline"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% if config.NOPAQUE_TRANSKRIBUS_ENABLED %}
|
||||||
|
<li>
|
||||||
|
<a class="btn-floating service-color darken tooltipped" data-position="left" data-tooltip="Transkribus HTR Pipeline" data-service="transkribus-htr-pipeline">
|
||||||
|
<i class="nopaque-icons service-icons" data-service="transkribus-htr-pipeline"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
<li>
|
||||||
|
<a class="btn-floating service-color darken tooltipped" data-position="left" data-tooltip="SpaCy NLP Pipeline" data-service="spacy-nlp-pipeline">
|
||||||
|
<i class="nopaque-icons service-icons" data-service="spacy-nlp-pipeline"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#
|
||||||
|
<div class="parallax-container">
|
||||||
|
<div class="parallax">
|
||||||
|
<img src="{{ url_for('static', filename='images/parallax_lq/01_books_antique_book_old.jpg') }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="fixed-action-btn">
|
||||||
|
<a class="btn-floating btn-large">
|
||||||
|
<i class="large material-icons">add</i>
|
||||||
|
</a>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a class="btn-floating service-color darken tooltipped" data-position="left" data-tooltip="File Setup Pipeline" data-service="file-setup-pipeline">
|
||||||
|
<i class="nopaque-icons service-icons" data-service="file-setup-pipeline"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="btn-floating service-color darken tooltipped" data-position="left" data-tooltip="Tesseract OCR Pipeline" data-service="tesseract-ocr-pipeline">
|
||||||
|
<i class="nopaque-icons service-icons" data-service="tesseract-ocr-pipeline"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% if config.NOPAQUE_TRANSKRIBUS_ENABLED %}
|
||||||
|
<li>
|
||||||
|
<a class="btn-floating service-color darken tooltipped" data-position="left" data-tooltip="Transkribus HTR Pipeline" data-service="transkribus-htr-pipeline">
|
||||||
|
<i class="nopaque-icons service-icons" data-service="transkribus-htr-pipeline"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
<li>
|
||||||
|
<a class="btn-floating service-color darken tooltipped" data-position="left" data-tooltip="SpaCy NLP Pipeline" data-service="spacy-nlp-pipeline">
|
||||||
|
<i class="nopaque-icons service-icons" data-service="spacy-nlp-pipeline"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="position: relative; top: -90px;">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="job-list" data-user-id="{{ current_user.hashid }}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card-action right-align">
|
||||||
|
<a class="btn modal-trigger waves-effect waves-light" data-target="create-job-modal">Create job<i class="material-icons right">add</i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
#}
|
||||||
|
{% endblock page_content %}
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
<div class="col s12" id="corpora">
|
<div class="col s12" id="corpora">
|
||||||
<h2>My Corpora</h2>
|
<h2>My Corpora</h2>
|
||||||
|
<p>Hallo <span class="pink-text">Audrey</span></p>
|
||||||
<p>Create a corpus to interactively perform linguistic analysis.</p>
|
<p>Create a corpus to interactively perform linguistic analysis.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -33,6 +34,7 @@
|
|||||||
pre-processing step. You will need the result of each step for the
|
pre-processing step. You will need the result of each step for the
|
||||||
next step.
|
next step.
|
||||||
</p>
|
</p>
|
||||||
|
<p><b>Where is my Job data?</b> Don't worry, please read <a href="{{ url_for('main.news', _anchor='april-2022-update') }}">this news</a> entry</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
@ -54,7 +56,7 @@
|
|||||||
<div class="card extension-selector hoverable service-color" data-service="tesseract-ocr-pipeline">
|
<div class="card extension-selector hoverable service-color" data-service="tesseract-ocr-pipeline">
|
||||||
<a href="{{ url_for('contributions.tesseract_ocr_pipeline_models') }}" style="position: absolute; width: 100%; height: 100%;"></a>
|
<a href="{{ url_for('contributions.tesseract_ocr_pipeline_models') }}" style="position: absolute; width: 100%; height: 100%;"></a>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<span class="card-title">Tesseract OCR Pipeline Models</span>
|
<span class="card-title" data-service="tesseract-ocr-pipeline">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>
|
<p>Here you can see and edit the models that you have created. You can also create new models.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,22 +20,22 @@
|
|||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col s12 m6 l3 center-align">
|
<div class="col s12 m6 l3 center-align">
|
||||||
<i class="large nopaque-icons primary-color-text" data-nopaque-icon="speedometer"></i><br>
|
<i class="large nopaque-icons primary-color-text">A</i><br>
|
||||||
<b class="primary-color-text">Speeds up your work</b>
|
<b class="primary-color-text">Speeds up your work</b>
|
||||||
<p class="light">All tools provided by nopaque are carefully selected to provide a complete tool suite without being held up by compatibility issues.</p>
|
<p class="light">All tools provided by nopaque are carefully selected to provide a complete tool suite without being held up by compatibility issues.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col s12 m6 l3 center-align">
|
<div class="col s12 m6 l3 center-align">
|
||||||
<i class="large nopaque-icons primary-color-text" data-nopaque-icon="cloud-processes"></i><br>
|
<i class="large nopaque-icons primary-color-text">B</i><br>
|
||||||
<b class="primary-color-text">Cloud infrastructure</b>
|
<b class="primary-color-text">Cloud infrastructure</b>
|
||||||
<p class="light">All computational work is processed within nopaque’s cloud infrastructure. You don't need to install any software. Great, right?</p>
|
<p class="light">All computational work is processed within nopaque’s cloud infrastructure. You don't need to install any software. Great, right?</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col s12 m6 l3 center-align">
|
<div class="col s12 m6 l3 center-align">
|
||||||
<i class="large nopaque-icons primary-color-text" data-nopaque-icon="account-circle"></i><br>
|
<i class="large nopaque-icons primary-color-text">C</i><br>
|
||||||
<b class="primary-color-text">User friendly</b>
|
<b class="primary-color-text">User friendly</b>
|
||||||
<p class="light">You can start right away without having to read mile-long manuals. All services come with default settings that make it easy for you to just get going. Also great, right?</p>
|
<p class="light">You can start right away without having to read mile-long manuals. All services come with default settings that make it easy for you to just get going. Also great, right?</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col s12 m6 l3 center-align">
|
<div class="col s12 m6 l3 center-align">
|
||||||
<i class="large nopaque-icons primary-color-text" data-nopaque-icon="meshed-gears"></i><br>
|
<i class="large nopaque-icons primary-color-text">D</i><br>
|
||||||
<b class="primary-color-text">Meshing processes</b>
|
<b class="primary-color-text">Meshing processes</b>
|
||||||
<p class="light">No matter where you step in, nopaque facilitates and accompanies your research. Its workflow perfectly ties in with your research process.</p>
|
<p class="light">No matter where you step in, nopaque facilitates and accompanies your research. Its workflow perfectly ties in with your research process.</p>
|
||||||
</div>
|
</div>
|
||||||
|
179
config.py
179
config.py
@ -1,54 +1,47 @@
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
from flask import Flask
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||||
ENV_FILE = os.path.join(BASE_DIR, 'nopaque.env')
|
load_dotenv(os.path.join(basedir, 'nopaque.env'))
|
||||||
|
|
||||||
|
|
||||||
if os.path.isfile(ENV_FILE):
|
|
||||||
load_dotenv(ENV_FILE)
|
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
''' Configuration class for the Flask application. '''
|
''' APIFairy '''
|
||||||
|
|
||||||
# region APIFairy
|
|
||||||
APIFAIRY_TITLE = 'nopaque'
|
APIFAIRY_TITLE = 'nopaque'
|
||||||
APIFAIRY_VERSION = '0.0.1'
|
APIFAIRY_VERSION = '0.0.1'
|
||||||
APIFAIRY_APISPEC_PATH = '/api/apispec.json'
|
|
||||||
APIFAIRY_UI = 'swagger_ui'
|
APIFAIRY_UI = 'swagger_ui'
|
||||||
APIFAIRY_UI_PATH = '/api/docs'
|
APIFAIRY_APISPEC_PATH = '/api/apispec.json'
|
||||||
# endregion APIFairy
|
APIFAIRY_UI_PATH = '/api'
|
||||||
|
|
||||||
|
''' # Flask # '''
|
||||||
# region Flask
|
APPLICATION_ROOT = os.environ.get('APPLICATION_ROOT', '/')
|
||||||
DEBUG = os.environ.get('FLASK_DEBUG', 'false').lower() == 'true'
|
|
||||||
PREFERRED_URL_SCHEME = os.environ.get('PREFERRED_URL_SCHEME', 'http')
|
PREFERRED_URL_SCHEME = os.environ.get('PREFERRED_URL_SCHEME', 'http')
|
||||||
SECRET_KEY = os.environ.get('SECRET_KEY', 'hard to guess string')
|
SECRET_KEY = os.environ.get('SECRET_KEY', 'hard to guess string')
|
||||||
SERVER_NAME = os.environ.get('SERVER_NAME', 'localhost:5000')
|
SERVER_NAME = os.environ.get('SERVER_NAME', 'localhost:5000')
|
||||||
SESSION_COOKIE_SECURE = os.environ.get('SESSION_COOKIE_SECURE', 'false').lower() == 'true'
|
SESSION_COOKIE_SECURE = \
|
||||||
# endregion Flask
|
os.environ.get('SESSION_COOKIE_SECURE', 'false').lower() == 'true'
|
||||||
|
|
||||||
|
''' # Flask-APScheduler # '''
|
||||||
|
JOBS = []
|
||||||
|
|
||||||
# region Flask-Assets
|
''' # Flask-Assets '''
|
||||||
ASSETS_DEBUG = os.environ.get('ASSETS_DEBUG', 'false').lower() == 'true'
|
ASSETS_DEBUG = os.environ.get('ASSETS_DEBUG', 'false').lower() == 'true'
|
||||||
# endregion Flask-Assets
|
|
||||||
|
|
||||||
|
''' # Flask-Hashids '''
|
||||||
# region Flask-Hashids
|
|
||||||
HASHIDS_MIN_LENGTH = int(os.environ.get('HASHIDS_MIN_LENGTH', '16'))
|
HASHIDS_MIN_LENGTH = int(os.environ.get('HASHIDS_MIN_LENGTH', '16'))
|
||||||
HASHIDS_SALT = os.environ.get('HASHIDS_SALT', 'hard to guess string')
|
HASHIDS_SALT = os.environ.get('HASHIDS_SALT', 'hard to guess string')
|
||||||
# endregion Flask-Hashids
|
|
||||||
|
|
||||||
|
''' # Flask-Login # '''
|
||||||
|
REMEMBER_COOKIE_SECURE = \
|
||||||
|
os.environ.get('REMEMBER_COOKIE_SECURE', 'false').lower() == 'true'
|
||||||
|
|
||||||
# region Flask-Login
|
''' # Flask-Mail # '''
|
||||||
REMEMBER_COOKIE_SECURE = os.environ.get('REMEMBER_COOKIE_SECURE', 'false').lower() == 'true'
|
|
||||||
# endregion Flask-Login
|
|
||||||
|
|
||||||
|
|
||||||
# region Flask-Mail
|
|
||||||
MAIL_DEFAULT_SENDER = os.environ.get('MAIL_DEFAULT_SENDER')
|
MAIL_DEFAULT_SENDER = os.environ.get('MAIL_DEFAULT_SENDER')
|
||||||
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
|
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
|
||||||
MAIL_SERVER = os.environ.get('MAIL_SERVER')
|
MAIL_SERVER = os.environ.get('MAIL_SERVER')
|
||||||
@ -56,53 +49,125 @@ class Config:
|
|||||||
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
|
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
|
||||||
MAIL_USE_SSL = os.environ.get('MAIL_USE_SSL', 'false').lower() == 'true'
|
MAIL_USE_SSL = os.environ.get('MAIL_USE_SSL', 'false').lower() == 'true'
|
||||||
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'false').lower() == 'true'
|
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'false').lower() == 'true'
|
||||||
# endregion Flask-Mail
|
|
||||||
|
|
||||||
|
''' # Flask-SQLAlchemy # '''
|
||||||
# region Flask-SQLAlchemy
|
SQLALCHEMY_DATABASE_URI = \
|
||||||
SQLALCHEMY_DATABASE_URI = os.environ.get(
|
os.environ.get('SQLALCHEMY_DATABASE_URI') \
|
||||||
'SQLALCHEMY_DATABASE_URI',
|
or f'sqlite:///{os.path.join(basedir, "data.sqlite")}'
|
||||||
f'sqlite:///{BASE_DIR}/data.sqlite'
|
|
||||||
)
|
|
||||||
SQLALCHEMY_RECORD_QUERIES = True
|
SQLALCHEMY_RECORD_QUERIES = True
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
# endregion Flask-SQLAlchemy
|
|
||||||
|
|
||||||
|
''' # nopaque # '''
|
||||||
# region nopaque
|
|
||||||
NOPAQUE_ADMIN = os.environ.get('NOPAQUE_ADMIN')
|
NOPAQUE_ADMIN = os.environ.get('NOPAQUE_ADMIN')
|
||||||
NOPAQUE_DATA_DIR = Path(os.environ.get('NOPAQUE_DATA_PATH', '/mnt/nopaque'))
|
NOPAQUE_DATA_DIR = Path(os.environ.get('NOPAQUE_DATA_PATH', '/mnt/nopaque'))
|
||||||
NOPAQUE_IS_PRIMARY_INSTANCE = os.environ.get('NOPAQUE_IS_PRIMARY_INSTANCE', 'true').lower() == 'true'
|
NOPAQUE_IS_PRIMARY_INSTANCE = \
|
||||||
|
os.environ.get('NOPAQUE_IS_PRIMARY_INSTANCE', 'true').lower() == 'true'
|
||||||
NOPAQUE_MAIL_SUBJECT_PREFIX = '[nopaque]'
|
NOPAQUE_MAIL_SUBJECT_PREFIX = '[nopaque]'
|
||||||
NOPAQUE_SERVICE_DESK = 'gitlab-ub-incoming+sfb1288inf-nopaque-1324-issue-@jura.uni-bielefeld.de' # noqa
|
NOPAQUE_SERVICE_DESK = 'gitlab-ub-incoming+sfb1288inf-nopaque-1324-issue-@jura.uni-bielefeld.de' # noqa
|
||||||
NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI = os.environ.get('NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI')
|
NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI = \
|
||||||
|
os.environ.get('NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI')
|
||||||
|
|
||||||
|
NOPAQUE_JOB_EXPIRATION_ENABLED = os.environ.get('NOPAQUE_JOB_EXPIRATION_ENABLED', 'true').lower() == 'true'
|
||||||
|
NOPAQUE_JOB_EXPIRATION_TIME = int(os.environ.get('NOPAQUE_JOB_EXPIRATION_TIME', '120'))
|
||||||
|
|
||||||
NOPAQUE_DOCKER_REGISTRY = 'gitlab.ub.uni-bielefeld.de:4567'
|
NOPAQUE_DOCKER_REGISTRY = 'gitlab.ub.uni-bielefeld.de:4567'
|
||||||
NOPAQUE_DOCKER_IMAGE_PREFIX = f'{NOPAQUE_DOCKER_REGISTRY}/sfb1288inf/'
|
NOPAQUE_DOCKER_IMAGE_PREFIX = f'{NOPAQUE_DOCKER_REGISTRY}/sfb1288inf/'
|
||||||
NOPAQUE_DOCKER_NETWORK_NAME = os.environ.get('DOCKER_NETWORK_NAME', 'nopaque')
|
NOPAQUE_DOCKER_NETWORK_NAME = \
|
||||||
NOPAQUE_DOCKER_REGISTRY_USERNAME = os.environ.get('NOPAQUE_DOCKER_REGISTRY_USERNAME')
|
os.environ.get('DOCKER_NETWORK_NAME', 'nopaque')
|
||||||
NOPAQUE_DOCKER_REGISTRY_PASSWORD = os.environ.get('NOPAQUE_DOCKER_REGISTRY_PASSWORD')
|
NOPAQUE_DOCKER_REGISTRY_USERNAME = \
|
||||||
|
os.environ.get('NOPAQUE_DOCKER_REGISTRY_USERNAME')
|
||||||
|
NOPAQUE_DOCKER_REGISTRY_PASSWORD = \
|
||||||
|
os.environ.get('NOPAQUE_DOCKER_REGISTRY_PASSWORD')
|
||||||
|
|
||||||
NOPAQUE_LOG_DATE_FORMAT = os.environ.get(
|
NOPAQUE_LOG_DATE_FORMAT = \
|
||||||
'NOPAQUE_LOG_DATE_FORMAT',
|
os.environ.get('NOPAQUE_LOG_DATE_FORMAT', '%Y-%m-%d %H:%M:%S')
|
||||||
'%Y-%m-%d %H:%M:%S'
|
|
||||||
)
|
|
||||||
NOPAQUE_LOG_FORMAT = os.environ.get(
|
NOPAQUE_LOG_FORMAT = os.environ.get(
|
||||||
'NOPAQUE_LOG_DATE_FORMAT',
|
'NOPAQUE_LOG_DATE_FORMAT',
|
||||||
'[%(asctime)s] %(levelname)s in %(pathname)s (function: %(funcName)s, line: %(lineno)d): %(message)s'
|
'[%(asctime)s] %(levelname)s in '
|
||||||
|
'%(pathname)s (function: %(funcName)s, line: %(lineno)d): %(message)s'
|
||||||
)
|
)
|
||||||
NOPAQUE_LOG_LEVEL = os.environ.get('NOPAQUE_LOG_LEVEL', 'WARNING')
|
NOPAQUE_LOG_FILE_ENABLED = \
|
||||||
|
os.environ.get('NOPAQUE_LOG_FILE_ENABLED', 'false').lower() == 'true'
|
||||||
|
NOPAQUE_LOG_FILE_DIR = Path(os.environ.get('NOPAQUE_LOG_FILE_DIR', '/var/log/nopaque'))
|
||||||
|
NOPAQUE_LOG_FILE_LEVEL = \
|
||||||
|
os.environ.get('NOPAQUE_LOG_FILE_LEVEL', None)
|
||||||
|
NOPAQUE_LOG_STDERR_ENABLED = \
|
||||||
|
os.environ.get('NOPAQUE_LOG_STDERR_ENABLED', 'true').lower() == 'true'
|
||||||
|
NOPAQUE_LOG_STDERR_LEVEL = \
|
||||||
|
os.environ.get('NOPAQUE_LOG_STDERR_LEVEL', None)
|
||||||
|
|
||||||
NOPAQUE_PROXY_FIX_ENABLED = os.environ.get('NOPAQUE_PROXY_FIX_ENABLED', 'false').lower() == 'true'
|
NOPAQUE_PROXY_FIX_ENABLED = \
|
||||||
NOPAQUE_PROXY_FIX_X_FOR = int(os.environ.get('NOPAQUE_PROXY_FIX_X_FOR', '0'))
|
os.environ.get('NOPAQUE_PROXY_FIX_ENABLED', 'false').lower() == 'true'
|
||||||
NOPAQUE_PROXY_FIX_X_HOST = int(os.environ.get('NOPAQUE_PROXY_FIX_X_HOST', '0'))
|
NOPAQUE_PROXY_FIX_X_FOR = \
|
||||||
NOPAQUE_PROXY_FIX_X_PORT = int(os.environ.get('NOPAQUE_PROXY_FIX_X_PORT', '0'))
|
int(os.environ.get('NOPAQUE_PROXY_FIX_X_FOR', '0'))
|
||||||
NOPAQUE_PROXY_FIX_X_PREFIX = int(os.environ.get('NOPAQUE_PROXY_FIX_X_PREFIX', '0'))
|
NOPAQUE_PROXY_FIX_X_HOST = \
|
||||||
NOPAQUE_PROXY_FIX_X_PROTO = int(os.environ.get('NOPAQUE_PROXY_FIX_X_PROTO', '0'))
|
int(os.environ.get('NOPAQUE_PROXY_FIX_X_HOST', '0'))
|
||||||
|
NOPAQUE_PROXY_FIX_X_PORT = \
|
||||||
|
int(os.environ.get('NOPAQUE_PROXY_FIX_X_PORT', '0'))
|
||||||
|
NOPAQUE_PROXY_FIX_X_PREFIX = \
|
||||||
|
int(os.environ.get('NOPAQUE_PROXY_FIX_X_PREFIX', '0'))
|
||||||
|
NOPAQUE_PROXY_FIX_X_PROTO = \
|
||||||
|
int(os.environ.get('NOPAQUE_PROXY_FIX_X_PROTO', '0'))
|
||||||
|
|
||||||
NOPAQUE_TRANSKRIBUS_ENABLED = os.environ.get('NOPAQUE_TRANSKRIBUS_ENABLED', 'false').lower() == 'true'
|
NOPAQUE_TRANSKRIBUS_ENABLED = \
|
||||||
|
os.environ.get('NOPAQUE_TRANSKRIBUS_ENABLED', 'false').lower() == 'true'
|
||||||
NOPAQUE_READCOOP_USERNAME = os.environ.get('NOPAQUE_READCOOP_USERNAME')
|
NOPAQUE_READCOOP_USERNAME = os.environ.get('NOPAQUE_READCOOP_USERNAME')
|
||||||
NOPAQUE_READCOOP_PASSWORD = os.environ.get('NOPAQUE_READCOOP_PASSWORD')
|
NOPAQUE_READCOOP_PASSWORD = os.environ.get('NOPAQUE_READCOOP_PASSWORD')
|
||||||
|
|
||||||
NOPAQUE_VERSION='1.0.2'
|
NOPAQUE_VERSION='1.0.2'
|
||||||
# endregion nopaque
|
|
||||||
|
@staticmethod
|
||||||
|
def init_app(app: Flask):
|
||||||
|
for handler in app.logger.handlers:
|
||||||
|
app.logger.removeHandler(handler)
|
||||||
|
|
||||||
|
log_formatter = logging.Formatter(
|
||||||
|
fmt=app.config['NOPAQUE_LOG_FORMAT'],
|
||||||
|
datefmt=app.config['NOPAQUE_LOG_DATE_FORMAT']
|
||||||
|
)
|
||||||
|
|
||||||
|
if app.config['NOPAQUE_LOG_STDERR_ENABLED']:
|
||||||
|
log_stderr_level: str | None = app.config['NOPAQUE_LOG_STDERR_LEVEL']
|
||||||
|
stream_handler = logging.StreamHandler()
|
||||||
|
stream_handler.setFormatter(log_formatter)
|
||||||
|
if log_stderr_level is not None:
|
||||||
|
stream_handler.setLevel(log_stderr_level)
|
||||||
|
app.logger.addHandler(stream_handler)
|
||||||
|
|
||||||
|
if app.config['NOPAQUE_LOG_FILE_ENABLED']:
|
||||||
|
log_file_dir: Path = app.config['NOPAQUE_LOG_FILE_DIR']
|
||||||
|
log_file_level: str | None = app.config['NOPAQUE_LOG_FILE_LEVEL']
|
||||||
|
if not log_file_dir.exists():
|
||||||
|
log_file_dir.mkdir()
|
||||||
|
rotating_file_handler = RotatingFileHandler(
|
||||||
|
log_file_dir / 'nopaque.log',
|
||||||
|
maxBytes=10_240,
|
||||||
|
backupCount=10
|
||||||
|
)
|
||||||
|
rotating_file_handler.setFormatter(log_formatter)
|
||||||
|
if log_file_level is not None:
|
||||||
|
rotating_file_handler.setLevel(log_file_level)
|
||||||
|
app.logger.addHandler(rotating_file_handler)
|
||||||
|
|
||||||
|
if app.config['NOPAQUE_PROXY_FIX_ENABLED']:
|
||||||
|
# Set up and apply the ProxyFix middleware according to the
|
||||||
|
# corresponding (NOPAQUE_PROXY_FIX_*) configurations
|
||||||
|
app.wsgi_app = ProxyFix(
|
||||||
|
app.wsgi_app,
|
||||||
|
x_for=app.config['NOPAQUE_PROXY_FIX_X_FOR'],
|
||||||
|
x_host=app.config['NOPAQUE_PROXY_FIX_X_HOST'],
|
||||||
|
x_port=app.config['NOPAQUE_PROXY_FIX_X_PORT'],
|
||||||
|
x_prefix=app.config['NOPAQUE_PROXY_FIX_X_PREFIX'],
|
||||||
|
x_proto=app.config['NOPAQUE_PROXY_FIX_X_PROTO']
|
||||||
|
)
|
||||||
|
|
||||||
|
if app.config['NOPAQUE_IS_PRIMARY_INSTANCE']:
|
||||||
|
app.config['JOBS'].append(
|
||||||
|
{
|
||||||
|
"id": "daemon",
|
||||||
|
"func": "app.daemon:daemon",
|
||||||
|
"args": (app,),
|
||||||
|
"trigger": "interval",
|
||||||
|
"seconds": 3,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
# Flask #
|
# Flask #
|
||||||
# https://flask.palletsprojects.com/en/1.1.x/config/ #
|
# https://flask.palletsprojects.com/en/1.1.x/config/ #
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
# DEFAULT: /
|
||||||
|
# APPLICATION_ROOT=
|
||||||
|
|
||||||
# CHOOSE ONE: http, https
|
# CHOOSE ONE: http, https
|
||||||
# DEFAULT: http
|
# DEFAULT: http
|
||||||
# PREFERRED_URL_SCHEME=
|
# PREFERRED_URL_SCHEME=
|
||||||
@ -138,9 +141,24 @@ NOPAQUE_DOCKER_REGISTRY_PASSWORD=
|
|||||||
# DEFAULT: [%(asctime)s] %(levelname)s in %(pathname)s (function: %(funcName)s, line: %(lineno)d): %(message)s
|
# DEFAULT: [%(asctime)s] %(levelname)s in %(pathname)s (function: %(funcName)s, line: %(lineno)d): %(message)s
|
||||||
# NOPAQUE_LOG_FORMAT=
|
# NOPAQUE_LOG_FORMAT=
|
||||||
|
|
||||||
|
# CHOOSE ONE: False, True
|
||||||
|
# DEFAULT: False
|
||||||
|
# NOPAQUE_LOG_FILE_ENABLED=
|
||||||
|
|
||||||
|
# DEFAULT: /var/log/nopaque
|
||||||
|
# NOPAQUE_LOG_FILE_DIR=
|
||||||
|
|
||||||
# DEFAULT: DEBUG if FLASK_DEBUG == True else WARNING
|
# DEFAULT: DEBUG if FLASK_DEBUG == True else WARNING
|
||||||
# CHOOSE ONE: CRITICAL, ERROR, WARNING, INFO, DEBUG
|
# CHOOSE ONE: CRITICAL, ERROR, WARNING, INFO, DEBUG
|
||||||
# NOPAQUE_LOG_LEVEL=
|
# NOPAQUE_LOG_FILE_LEVEL=
|
||||||
|
|
||||||
|
# CHOOSE ONE: False, True
|
||||||
|
# DEFAULT: True
|
||||||
|
# NOPAQUE_LOG_STDERR_ENABLED=
|
||||||
|
|
||||||
|
# DEFAULT: DEBUG if FLASK_DEBUG == True else WARNING
|
||||||
|
# CHOOSE ONE: CRITICAL, ERROR, WARNING, INFO, DEBUG
|
||||||
|
# NOPAQUE_LOG_STDERR_LEVEL=
|
||||||
|
|
||||||
# CHOOSE ONE: False, True
|
# CHOOSE ONE: False, True
|
||||||
# DEFAULT: False
|
# DEFAULT: False
|
||||||
|
Loading…
Reference in New Issue
Block a user