diff --git a/.env.tpl b/.env.tpl index ad27fb60..d05e16ae 100644 --- a/.env.tpl +++ b/.env.tpl @@ -91,9 +91,8 @@ MAIL_USERNAME= # Flask-SQLAlchemy # # https://flask-sqlalchemy.palletsprojects.com/en/2.x/config/ # ################################################################################ -# DEFAULT with development config: postgresql://nopaque:nopaque@db/nopaque_dev -# DEFAULT with production config: postgresql://nopaque:nopaque@db/nopaque -# DEFAULT with testing config: postgresql://nopaque:nopaque@db/nopaque_test +# DEFAULT: 'sqlite:////app.db' +# NOTE: Use `.` as # SQLALCHEMY_DATABASE_URI= @@ -105,10 +104,6 @@ MAIL_USERNAME= # EXAMPLE: admin.nopaque@example.com NOPAQUE_ADMIN= -# DEFAULT: development -# CHOOSE ONE: development, production, testing -# NOPAQUE_CONFIG= - # This email adress is used for the contact button in the nopaque footer. If # not set, no contact button is displayed. # DEFAULT: None @@ -124,24 +119,13 @@ NOPAQUE_ADMIN= # DEFAULT: True # NOPAQUE_DAEMON_ENABLED= -# The hostname or IP address for the server to listen on. -# DEFAULT: 0.0.0.0 -# NOTES: To use a domain locally, add any names that should route to the app to your hosts file. -# If nopaque is running in a Docker container, you propably want to use the default value. -# NOPAQUE_HOST= - -# The port number for the server to listen on. -# DEFAULT: 5000 -# NOTE: If nopaque is running in a Docker container, you propably want to use the default value. -# NOPAQUE_PORT= - # transport://[userid:password]@hostname[:port]/[virtual_host] NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI= # DEFAULT: %Y-%m-%d %H:%M:%S # NOPAQUE_LOG_DATE_FORMAT= -# DEFAULT: /nopaque.log ~ /home/nopaque/nopaque.log +# DEFAULT: /nopaque.log # NOTE: Use `.` as # NOPAQUE_LOG_FILE= diff --git a/.flaskenv b/.flaskenv new file mode 100644 index 00000000..1fd672d3 --- /dev/null +++ b/.flaskenv @@ -0,0 +1 @@ +FLASK_APP=nopaque.py diff --git a/app/__init__.py b/app/__init__.py index 4649a090..c03a25ea 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,7 +1,8 @@ -from config import config +from config import Config from flask import Flask from flask_login import LoginManager from flask_mail import Mail +from flask_migrate import Migrate from flask_paranoid import Paranoid from flask_socketio import SocketIO from flask_sqlalchemy import SQLAlchemy @@ -10,23 +11,26 @@ import flask_assets assets = flask_assets.Environment() db = SQLAlchemy() -login_manager = LoginManager() -login_manager.login_view = 'auth.login' +login = LoginManager() +login.login_view = 'auth.login' +login.login_message = 'Please log in to access this page.' mail = Mail() +migrate = Migrate() paranoid = Paranoid() paranoid.redirect_view = '/' socketio = SocketIO() -def create_app(config_name): +def create_app(config_class=Config): app = Flask(__name__) - app.config.from_object(config[config_name]) + app.config.from_object(config_class) assets.init_app(app) - config[config_name].init_app(app) + config_class.init_app(app) db.init_app(app) - login_manager.init_app(app) + login.init_app(app) mail.init_app(app) + migrate.init_app(app, db) paranoid.init_app(app) socketio.init_app( app, message_queue=app.config['NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI']) diff --git a/app/cli.py b/app/cli.py new file mode 100644 index 00000000..7c0782b2 --- /dev/null +++ b/app/cli.py @@ -0,0 +1,25 @@ +from .models import Role +from flask_migrate import upgrade + + +def register(app): + @app.cli.command() + def deploy(): + """Run deployment tasks.""" + # migrate database to latest revision + upgrade() + # create or update user roles + Role.insert_roles() + + @app.cli.command() + def tasks(): + from app.tasks import TaskRunner + task_runner = TaskRunner() + task_runner.run() + + @app.cli.command() + def test(): + """Run the unit tests.""" + import unittest + tests = unittest.TestLoader().discover('tests') + unittest.TextTestRunner(verbosity=2).run(tests) diff --git a/app/models.py b/app/models.py index abc6b9f2..5f79cb59 100644 --- a/app/models.py +++ b/app/models.py @@ -1,11 +1,11 @@ from datetime import datetime, timedelta from flask import current_app, url_for -from flask_login import UserMixin, AnonymousUserMixin +from flask_login import UserMixin from itsdangerous import BadSignature, TimedJSONWebSignatureSerializer from time import sleep from werkzeug.security import generate_password_hash, check_password_hash import xml.etree.ElementTree as ET -from . import db, login_manager +from . import db, login import base64 import logging import os @@ -285,18 +285,6 @@ class User(UserMixin, db.Model): return user -class AnonymousUser(AnonymousUserMixin): - ''' - Model replaces the default AnonymousUser. - ''' - - def can(self, permissions): - return False - - def is_administrator(self): - return False - - class JobInput(db.Model): ''' Class to define JobInputs. @@ -722,13 +710,6 @@ class QueryResult(db.Model): return ''.format(self.title) -''' -' Flask-Login is told to use the application’s custom anonymous user by setting -' its class in the login_manager.anonymous_user attribute. -''' -login_manager.anonymous_user = AnonymousUser - - -@login_manager.user_loader +@login.user_loader def load_user(user_id): return User.query.get(int(user_id)) diff --git a/config.py b/config.py index 913229cb..2c99b412 100644 --- a/config.py +++ b/config.py @@ -1,9 +1,11 @@ +from dotenv import load_dotenv from werkzeug.middleware.proxy_fix import ProxyFix import logging import os -ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) +basedir = os.path.abspath(os.path.dirname(__file__)) +load_dotenv(os.path.join(basedir, '.env')) class Config: @@ -33,6 +35,10 @@ class Config: MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'false').lower() == 'true' ''' # Flask-SQLAlchemy # ''' + SQLALCHEMY_DATABASE_URI = os.environ.get( + 'SQLALCHEMY_DATABASE_URI', + 'sqlite:///' + os.path.join(basedir, 'app.db') + ) SQLALCHEMY_RECORD_QUERIES = True SQLALCHEMY_TRACK_MODIFICATIONS = False @@ -52,7 +58,7 @@ class Config: 'datefmt': os.environ.get('NOPAQUE_LOG_DATE_FORMAT', '%Y-%m-%d %H:%M:%S'), 'filename': os.environ.get('NOPAQUE_LOG_FILE', - os.path.join(ROOT_DIR, 'nopaque.log')), + os.path.join(basedir, 'nopaque.log')), 'format': os.environ.get( 'NOPAQUE_LOG_FORMAT', '[%(asctime)s] %(levelname)s in ' @@ -72,37 +78,3 @@ class Config: 'x_proto': int(os.environ.get('NOPAQUE_PROXY_FIX_X_PROTO', '0')) } app.wsgi_app = ProxyFix(app.wsgi_app, **proxy_fix_kwargs) - - -class DevelopmentConfig(Config): - ''' # Flask # ''' - DEBUG = True - - ''' # Flask-SQLAlchemy # ''' - SQLALCHEMY_DATABASE_URI = os.environ.get( - 'SQLALCHEMY_DATABASE_URI', - 'postgresql://nopaque:nopaque@db/nopaque_dev' - ) - - -class ProductionConfig(Config): - ''' # Flask-SQLAlchemy # ''' - SQLALCHEMY_DATABASE_URI = os.environ.get( - 'SQLALCHEMY_DATABASE_URI', 'postgresql://nopaque:nopaque@db/nopaque') - - -class TestingConfig(Config): - ''' # Flask # ''' - TESTING = True - WTF_CSRF_ENABLED = False - - ''' # Flask-SQLAlchemy # ''' - SQLALCHEMY_DATABASE_URI = os.environ.get( - 'SQLALCHEMY_DATABASE_URI', - 'postgresql://nopaque:nopaque@db/nopaque_test' - ) - - -config = {'development': DevelopmentConfig, - 'production': ProductionConfig, - 'testing': TestingConfig} diff --git a/nopaque.py b/nopaque.py index 53c75e29..1e32e381 100644 --- a/nopaque.py +++ b/nopaque.py @@ -6,23 +6,13 @@ import eventlet eventlet.monkey_patch() -from dotenv import load_dotenv # noqa -import os # noqa - -# Load environment variables -DOTENV_FILE = os.path.join(os.path.dirname(__file__), '.env') -if os.path.exists(DOTENV_FILE): - load_dotenv(DOTENV_FILE) - - -from app import create_app, db, socketio # noqa +from app import db, cli, create_app, socketio # noqa from app.models import (Corpus, CorpusFile, Job, JobInput, JobResult, QueryResult, Role, User) # noqa -from flask_migrate import Migrate, upgrade # noqa -app = create_app(os.environ.get('NOPAQUE_CONFIG', 'development')) -migrate = Migrate(app, db, compare_type=True) +app = create_app() +cli.register(app) @app.shell_context_processor @@ -38,32 +28,7 @@ def make_shell_context(): 'User': User} -@app.cli.command() -def deploy(): - """Run deployment tasks.""" - # migrate database to latest revision - upgrade() - - # create or update user roles - Role.insert_roles() - - -@app.cli.command() -def tasks(): - from app.tasks import TaskRunner - task_runner = TaskRunner() - task_runner.run() - - -@app.cli.command() -def test(): - """Run the unit tests.""" - import unittest - tests = unittest.TestLoader().discover('tests') - unittest.TextTestRunner(verbosity=2).run(tests) - - if __name__ == '__main__': - host = os.environ.get('NOPAQUE_HOST', '0.0.0.0') - port = int(os.environ.get('NOPAQUE_PORT', '5000')) + host = '0.0.0.0' + port = 5000 socketio.run(app, host=host, port=port) diff --git a/tests/__init__.py b/tests/__init__.py index e69de29b..b36bebf9 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,6 @@ +from config import Config + + +class TestConfig(Config): + TESTING = True + SQLALCHEMY_DATABASE_URI = 'sqlite://' diff --git a/tests/test_basics.py b/tests/test_basics.py index e52943de..df9d0ff5 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -1,11 +1,12 @@ -import unittest -from flask import current_app from app import create_app, db +from flask import current_app +from . import TestConfig +import unittest class BasicsTestCase(unittest.TestCase): def setUp(self): - self.app = create_app('testing') + self.app = create_app(TestConfig) self.app_context = self.app.app_context() self.app_context.push() db.create_all() diff --git a/tests/test_client.py b/tests/test_client.py index e4c0738c..89aaa74a 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,10 +1,11 @@ -import unittest from app import create_app, db +from . import TestConfig +import unittest class FlaskClientTestCase(unittest.TestCase): def setUp(self): - self.app = create_app('testing') + self.app = create_app(TestConfig) self.app_context = self.app.app_context() self.app_context.push() db.create_all() @@ -48,7 +49,8 @@ class FlaskClientTestCase(unittest.TestCase): 'password2': 'cat' }) self.assertEqual(response.status_code, 200) - self.assertTrue('Usernames must have only letters, numbers, dots or underscores' in response.get_data(as_text=True)) + self.assertTrue( + 'Usernames must have only letters, numbers, dots or underscores' in response.get_data(as_text=True)) def test_register_false_email(self): # register a new account with wrong username @@ -59,7 +61,8 @@ class FlaskClientTestCase(unittest.TestCase): 'password2': 'cat' }) self.assertEqual(response.status_code, 200) - self.assertTrue('Invalid email address.' in response.get_data(as_text=True)) + self.assertTrue( + 'Invalid email address.' in response.get_data(as_text=True)) def test_duplicates(self): # tries to register an account that has already been registered @@ -78,7 +81,8 @@ class FlaskClientTestCase(unittest.TestCase): 'password2': 'cat' }) self.assertEqual(response.status_code, 200) - self.assertTrue('Username already in use.' in response.get_data(as_text=True)) + self.assertTrue( + 'Username already in use.' in response.get_data(as_text=True)) response = self.client.post('/auth/register', data={ 'email': 'john@example.com', 'username': 'johnsmith', @@ -86,7 +90,8 @@ class FlaskClientTestCase(unittest.TestCase): 'password2': 'cat' }) self.assertEqual(response.status_code, 200) - self.assertTrue('Email already registered.' in response.get_data(as_text=True)) + self.assertTrue( + 'Email already registered.' in response.get_data(as_text=True)) def test_admin_forbidden(self): response = self.client.post('/auth/login', data={ diff --git a/tests/test_user_model.py b/tests/test_user_model.py index adc650bd..30066087 100644 --- a/tests/test_user_model.py +++ b/tests/test_user_model.py @@ -1,12 +1,13 @@ -import unittest -import time from app import create_app, db from app.models import User, AnonymousUser, Role, Permission +from . import TestConfig +import time +import unittest class UserModelTestCase(unittest.TestCase): def setUp(self): - self.app = create_app('testing') + self.app = create_app(TestConfig) self.app_context = self.app.app_context() self.app_context.push() db.create_all()