diff --git a/.env_example b/.env_example new file mode 100644 index 0000000000000000000000000000000000000000..f328a3ed246062bbb393b64f6aff72a80b68a311 --- /dev/null +++ b/.env_example @@ -0,0 +1,17 @@ +### Flask ### +FLASK_CONFIG=production +# SECRET_KEY= + +### Flask-Mail ### +MAIL_SERVER=smtp.example.com +MAIL_PORT=587 +MAIL_USE_TLS=true +MAIL_USERNAME=username@example.com +MAIL_PASSWORD=password +MAIL_DEFAULT_SENDER=username@example.com + +### Opaque ### +OPAQUE_ADMIN=admin.opaque@example.com + +### Flask-SQLAlchemy ### +# SQLALCHEMY_DATABASE_URI= diff --git a/.flaskenv b/.flaskenv index f88b6e5b9015d74cef7f8cb429bbf93297cb1d75..47093cdaf93d07580a062a158c87bbed2c7fa72e 100644 --- a/.flaskenv +++ b/.flaskenv @@ -1,2 +1,2 @@ +### Flask ### FLASK_APP=opaque.py -FLASK_ENV=development diff --git a/Dockerfile b/Dockerfile index e6a7ea0b148347395aa0c5c1f4ba7c78f959a6e6..e588a372cde57808b9dd455fc902ae487e09b248 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,30 @@ -# pull official base image -FROM python:3.6.9 +FROM python:3.6-alpine -# set environment varibles -ENV PYTHONDONTWRITEBYTECODE 1 -ENV PYTHONUNBUFFERED 1 -# set work directory -WORKDIR /opaque +RUN apk add build-base -# Copy the current directory contents into the container at /daemon -COPY . /opaque -# Install requirements -RUN pip install --trusted-host pypi.python.org -r requirements.txt +RUN adduser -D opaque +USER opaque -# set permissions for entrypoint -RUN chmod a+x flask-entrypoint.sh + +WORKDIR /home/opaque + + +COPY app app +COPY migrations migrations +COPY opaque.py config.py ./ +COPY requirements.txt requirements.txt + + +RUN python -m venv venv && \ + venv/bin/pip install -r requirements.txt + + +COPY docker-entrypoint.sh /usr/local/bin/ + + +EXPOSE 5000 + + +ENTRYPOINT ["docker-entrypoint.sh"] diff --git a/app/email.py b/app/email.py index 1e3fb3abc49e28784e2673937bfd1a27b415a6c9..072b7bc37aabf70f94b0b7a17154e680d29ece9c 100644 --- a/app/email.py +++ b/app/email.py @@ -10,11 +10,7 @@ def send_async_email(app, msg): def send_email(to, subject, template, **kwargs): - subject = '{} {}'.format(current_app.config['OPAQUE_MAIL_SUBJECT_PREFIX'], - subject) - msg = Message(subject, - sender=current_app.config['OPAQUE_MAIL_SENDER'], - recipients=[to]) + msg = Message('[Opaque] {}'.format(subject), recipients=[to]) msg.body = render_template(template + '.txt.j2', **kwargs) msg.html = render_template(template + '.html.j2', **kwargs) thr = Thread(target=send_async_email, diff --git a/app/main/views.py b/app/main/views.py index efb2b0bd349cf750ada2c7ccd2ee28d82ec25d19..9acc9061f80c39e76307583b957aa3620274cc88 100644 --- a/app/main/views.py +++ b/app/main/views.py @@ -21,7 +21,7 @@ def corpus(corpus_id): print('Corpus not found.') abort(404) - dir = os.path.join(current_app.config['OPAQUE_STORAGE'], + dir = os.path.join(current_app.config['OPAQUE_STORAGE_DIRECTORY'], str(current_user.id), 'corpora', str(corpus.id)) @@ -44,7 +44,7 @@ def corpus_download(corpus_id): if not file or not corpus: print('File not found.') abort(404) - dir = os.path.join(current_app.config['OPAQUE_STORAGE'], + dir = os.path.join(current_app.config['OPAQUE_STORAGE_DIRECTORY'], str(current_user.id), 'corpora', str(corpus.id)) @@ -66,7 +66,7 @@ def dashboard(): db.session.add(corpus) db.session.commit() - dir = os.path.join(app.config['OPAQUE_STORAGE'], + dir = os.path.join(app.config['OPAQUE_STORAGE_DIRECTORY'], str(corpus.user_id), 'corpora', str(corpus.id)) @@ -96,7 +96,7 @@ def job(job_id): print('Job not found.') abort(404) - dir = os.path.join(current_app.config['OPAQUE_STORAGE'], + dir = os.path.join(current_app.config['OPAQUE_STORAGE_DIRECTORY'], str(current_user.id), 'jobs', str(job.id)) @@ -130,7 +130,7 @@ def job_download(job_id): if not file or not job: print('File not found.') abort(404) - dir = os.path.join(current_app.config['OPAQUE_STORAGE'], + dir = os.path.join(current_app.config['OPAQUE_STORAGE_DIRECTORY'], str(current_user.id), 'jobs', str(job.id)) diff --git a/app/services/views.py b/app/services/views.py index 96247d57aeba03179934b0212ff54bab440d3478..52c36084dc626e8e9b130465f4b49d179f60244c 100644 --- a/app/services/views.py +++ b/app/services/views.py @@ -26,7 +26,7 @@ def nlp(): db.session.add(nlp_job) db.session.commit() - dir = os.path.join(current_app.config['OPAQUE_STORAGE'], + dir = os.path.join(current_app.config['OPAQUE_STORAGE_DIRECTORY'], str(nlp_job.user_id), 'jobs', str(nlp_job.id)) @@ -72,7 +72,7 @@ def ocr(): db.session.add(ocr_job) db.session.commit() - dir = os.path.join(current_app.config['OPAQUE_STORAGE'], + dir = os.path.join(current_app.config['OPAQUE_STORAGE_DIRECTORY'], str(ocr_job.user_id), 'jobs', str(ocr_job.id)) diff --git a/app/templates/main/jobs/job.html.j2 b/app/templates/main/jobs/job.html.j2 index a9bfd2b54c19519bfbd5801355855572b006f3cc..12865aa2a18e543d90245ec5ce9338176463ffb8 100644 --- a/app/templates/main/jobs/job.html.j2 +++ b/app/templates/main/jobs/job.html.j2 @@ -161,11 +161,12 @@ </div> </div> + <span class="card-title">Files</span> <table> <thead> <tr> - <th>Inputs</th> - <th>Results</th> + <th style="width: 50%;">Inputs</th> + <th style="width: 50%;">Results</th> </tr> </thead> <tbody> @@ -179,8 +180,6 @@ {% for result in files[file]['results'] %} <a href="{{ url_for('main.job_download', job_id=job.id, file=files[file]['results'][result]['path']) }}" class="waves-effect waves-light btn-small">{{ result }}</a> {% endfor %} - {% else %} - None {% endif %} </td> </tr> diff --git a/app/templates/services/nlp.html.j2 b/app/templates/services/nlp.html.j2 index 1ba9550b7eac2c1766f2e9c522b79ffd55d2eda9..8e394f084a93bb4f47477fc1fc58ca2fa4a2cead 100644 --- a/app/templates/services/nlp.html.j2 +++ b/app/templates/services/nlp.html.j2 @@ -13,56 +13,32 @@ <div class="col s12 m6"> <div class="card z-depth-0"> <div class="card-content"> - <span class="card-title"> - <i class="material-icons blue-grey-text text-darken-2 left">layers</i> - Tokenisierung - </span> - <p> - Aufteilung eines Textes in Sätze und Wörter. Dies - ist zur weiteren Verarbeitung notwendig. - </p> + <span class="card-title"><i class="material-icons blue-grey-text text-darken-2 left">layers</i>Tokenisierung</span> + <p>Aufteilung eines Textes in Sätze und Wörter. Dies ist zur weiteren Verarbeitung notwendig.</p> </div> </div> </div> <div class="col s12 m6"> <div class="card z-depth-0"> <div class="card-content"> - <span class="card-title"> - <i class="material-icons blue-grey-text text-darken-2 left">layers</i> - Lemmatisierung - </span> - <p> - Reduktion der Flexionsformen eines Wortes auf dessen - Grundform. - </p> + <span class="card-title"><i class="material-icons blue-grey-text text-darken-2 left">layers</i>Lemmatisierung</span> + <p>Reduktion der Flexionsformen eines Wortes auf dessen Grundform.<br><br></p> </div> </div> </div> <div class="col s12 m6"> <div class="card z-depth-0"> <div class="card-content"> - <span class="card-title"> - <i class="material-icons blue-grey-text text-darken-2 left">layers</i> - Part-of-speech-Tagging - </span> - <p> - Kontext- und definitionsbezogene Zuordnung von Wörtern - und Satzzeichen zu Wortarten. - </p> + <span class="card-title"><i class="material-icons blue-grey-text text-darken-2 left">layers</i>Part-of-speech-Tagging</span> + <p>Kontext- und definitionsbezogene Zuordnung von Wörtern und Satzzeichen zu Wortarten.</p> </div> </div> </div> <div class="col s12 m6"> <div class="card z-depth-0"> <div class="card-content"> - <span class="card-title"> - <i class="material-icons blue-grey-text text-darken-2 left">layers</i> - Eigennamenerkennung - </span> - <p> - Identifikation von Wörtern, die eine Entität - beschreiben, wie Firmen- und Personennamen. - </p> + <span class="card-title"><i class="material-icons blue-grey-text text-darken-2 left">layers</i>Eigennamenerkennung</span> + <p>Identifikation von Wörtern, die eine Entitätbeschreiben, wie Firmen- und Personennamen.</p> </div> </div> </div> diff --git a/app/templates/services/ocr.html.j2 b/app/templates/services/ocr.html.j2 index 2fc3198c37f24bd5e956435b0d2f5642c53c1877..3e27289814314ac626de7eb8a89e5ed5e5c5e3a4 100644 --- a/app/templates/services/ocr.html.j2 +++ b/app/templates/services/ocr.html.j2 @@ -14,56 +14,32 @@ <div class="col s12 m6"> <div class="card z-depth-0"> <div class="card-content"> - <span class="card-title"> - <i class="material-icons blue-grey-text text-darken-2 left">layers</i> - Eingabe von Bilddaten - </span> - <p> - Über ein Auftragsformular können Bilddaten in Form von - PDF-Dateien hochgeladen werden. - </p> + <span class="card-title"><i class="material-icons blue-grey-text text-darken-2 left">layers</i>Eingabe von Bilddaten</span> + <p>Über ein Auftragsformular können Bilddaten in Form von PDF-Dateien hochgeladen werden.</p> </div> </div> </div> <div class="col s12 m6"> <div class="card z-depth-0"> <div class="card-content"> - <span class="card-title"> - <i class="material-icons blue-grey-text text-darken-2 left">layers</i> - Optische Zeichenerkennung - </span> - <p> - Die optische Zeichenerkennung erfolgt in der - Recheninfrastruktur der Plattform. - </p> + <span class="card-title"><i class="material-icons blue-grey-text text-darken-2 left">layers</i>Optische Zeichenerkennung</span> + <p>Die optische Zeichenerkennung erfolgt in der Recheninfrastruktur der Plattform.</p> </div> </div> </div> <div class="col s12 m6"> <div class="card z-depth-0"> <div class="card-content"> - <span class="card-title"> - <i class="material-icons blue-grey-text text-darken-2 left">layers</i> - Fehlerkorrektur - </span> - <p> - Je nach Qualität der Eingabedaten kann es zu - Fehlern kommen, die korrigiert werden sollten. - </p> + <span class="card-title"><i class="material-icons blue-grey-text text-darken-2 left">layers</i>Fehlerkorrektur</span> + <p>Je nach Qualität der Eingabedaten kann es zu Fehlern kommen, die korrigiert werden sollten.</p> </div> </div> </div> <div class="col s12 m6"> <div class="card z-depth-0"> <div class="card-content"> - <span class="card-title"> - <i class="material-icons blue-grey-text text-darken-2 left">layers</i> - Weiterverarbeitung - </span> - <p> - Die Textdaten können weiterverarbeitet<a class="tooltipped" data-position="top" data-tooltip="Zum Beispiel durch die hier angebotene linguistische Datenverarbeitung."><sup>[*]</sup></a> - oder in dieser Form bereits genutzt<a class="tooltipped" data-position="top" data-tooltip="Zum Beispiel mit dem Programm "AntConc"."><sup>[*]</sup></a> werden. - </p> + <span class="card-title"><i class="material-icons blue-grey-text text-darken-2 left">layers</i>Weiterverarbeitung</span> + <p>Die Textdaten können weiterverarbeitet<a class="tooltipped" data-position="top" data-tooltip="Zum Beispiel durch die hier angebotene linguistische Datenverarbeitung."><sup>[*]</sup></a> oder in dieser Form bereits genutzt<a class="tooltipped" data-position="top" data-tooltip="Zum Beispiel mit dem Programm "AntConc"."><sup>[*]</sup></a> werden.</p> </div> </div> </div> diff --git a/config.py b/config.py index 1b3403011003d8c85032acc8d32bc7ba71bdbd64..f80e8f2743e849f54d04a5c31068e120cfebbd22 100644 --- a/config.py +++ b/config.py @@ -1,29 +1,34 @@ import os -basedir = os.path.abspath(os.path.dirname(__file__)) - - class Config: + ''' ### Flask ### ''' + SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string' + + ''' ### Flask-Mail ### ''' MAIL_SERVER = os.environ.get('MAIL_SERVER') MAIL_PORT = int(os.environ.get('MAIL_PORT')) MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS').lower() == 'true' MAIL_USERNAME = os.environ.get('MAIL_USERNAME') MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD') - OPAQUE_ADMIN = os.environ.get('OPAQUE_ADMIN') - OPAQUE_STORAGE = os.environ.get('OPAQUE_STORAGE') - OPAQUE_MAIL_SUBJECT_PREFIX = '[Opaque]' - OPAQUE_MAIL_SENDER = 'Opaque' - SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string' + MAIL_DEFAULT_SENDER = os.environ.get('MAIL_DEFAULT_SENDER') + + ''' ### Flask-SQLAlchemy ### ''' SQLALCHEMY_TRACK_MODIFICATIONS = False + ''' ### Opaque ### ''' + OPAQUE_ADMIN = os.environ.get('OPAQUE_ADMIN') + OPAQUE_STORAGE_DIRECTORY = '/opaque_storage' + @staticmethod def init_app(app): pass class DevelopmentConfig(Config): + ''' ### Flask ### ''' DEBUG = True +<<<<<<< HEAD # SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, # 'data_dev.sqlite') SQLALCHEMY_DATABASE_URI = \ @@ -34,22 +39,29 @@ class DevelopmentConfig(Config): port=os.environ.get('POSTGRES_PORT'), db=os.environ.get('POSTGRES_DB_NAME')) print(SQLALCHEMY_DATABASE_URI) +======= +>>>>>>> e3db2ecd1e52b4993b91e1a1c3501c0fc775de1d - -class TestingConfig(Config): - TESTING = True - SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \ - 'sqlite://' - WTF_CSRF_ENABLED = False + ''' ### Flask-SQLAlchemy ### ''' + SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format( + os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'data_dev.sqlite') + ) -# class ProductionConfig(Config): +class ProductionConfig(Config): + ''' ### Flask-SQLAlchemy ### ''' + SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI') config = { 'development': DevelopmentConfig, +<<<<<<< HEAD 'testing': TestingConfig, #'production': ProductionConfig, +======= + 'production': ProductionConfig, +>>>>>>> e3db2ecd1e52b4993b91e1a1c3501c0fc775de1d 'default': DevelopmentConfig } diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000000000000000000000000000000000000..535d86a0675c5438727ca300658882c64452364a --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# If no argument is given, start Opaque +if [ $# -eq 0 ] +then + venv/bin/python opaque.py +else + if [[ $1 == "--create-db" ]] + then + flask db init + flask db upgrade + fi +fi diff --git a/flask-entrypoint.sh b/flask-entrypoint.sh deleted file mode 100644 index ed4b193f9a836d4177495fc483e909972058693b..0000000000000000000000000000000000000000 --- a/flask-entrypoint.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -x - -python opaque.py diff --git a/opaque.py b/opaque.py index 3f8483afdbdc32c6be9ad16067663ca12f5b616d..8d4b01010a64cea0f553c3a81f384a6ec9e7ea86 100644 --- a/opaque.py +++ b/opaque.py @@ -1,7 +1,5 @@ import eventlet eventlet.monkey_patch() -from dotenv import load_dotenv -load_dotenv() from app import create_app, db, socketio from app.models import Corpus, User, Role, Permission, Job from flask_migrate import Migrate diff --git a/requirements.txt b/requirements.txt index 3fce165f3eb186f2fb15a8f2c4036a4a04e25e4b..a36d129ce19113878f34c43eb179b4d891ed1c85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,9 @@ Flask-SQLAlchemy Flask-Table Flask-WTF jsonpatch +<<<<<<< HEAD psycopg2 python-dotenv +======= +>>>>>>> e3db2ecd1e52b4993b91e1a1c3501c0fc775de1d redis