diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index b17be19d..1f7dcf6b 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -23,6 +23,10 @@ services: - "USAON_VTA_DB_SQLITE=true" # Enable the in-browser debugger: - "FLASK_DEBUG=True" + # NOTE: This is temporary until issue #144 is resolved + # Creds for talking to Google: + - "USAON_VTA_GOOGLE_CLIENT_ID=${USAON_VTA_GOOGLE_CLIENT_ID:?USAON_VTA_GOOGLE_CLIENT_ID must be set}" + - "USAON_VTA_GOOGLE_CLIENT_SECRET=${USAON_VTA_GOOGLE_CLIENT_SECRET:?USAON_VTA_GOOGLE_CLIENT_SECRET must be set}" # Development DANGER ZONE: Do not use the below settings except for diff --git a/scripts/invoke_in_container.sh b/scripts/invoke_in_container.sh index 35492c10..3643c10a 100755 --- a/scripts/invoke_in_container.sh +++ b/scripts/invoke_in_container.sh @@ -5,7 +5,7 @@ args="$@" # TODO: Do we need this? --user="${UID}" \ # Or this for wrapping the command? bash --login -c \ -docker-compose run \ +docker-compose run --rm \ usaon-vta-survey \ invoke $args diff --git a/tasks/db.py b/tasks/db.py index ceb5c9b5..2777f8a6 100644 --- a/tasks/db.py +++ b/tasks/db.py @@ -13,9 +13,11 @@ def init(ctx, load=True): ) return - from usaon_vta_survey import app + from usaon_vta_survey import create_app from usaon_vta_survey.util.db.setup import recreate_tables as recreate_tables_ + app = create_app() + # TODO: "Are you sure" confirmation? with app.app_context(): print('Recreating tables...') @@ -36,8 +38,10 @@ def load_reference_data(ctx): ) return - from usaon_vta_survey import app + from usaon_vta_survey import create_app from usaon_vta_survey.util.db.setup import populate_reference_data + app = create_app() + with app.app_context(): populate_reference_data() diff --git a/tasks/test.py b/tasks/test.py index 75036ad4..5d32eec9 100644 --- a/tasks/test.py +++ b/tasks/test.py @@ -1,4 +1,3 @@ -import os import sys from pathlib import Path @@ -6,10 +5,6 @@ from .util import print_and_run -# NOTE: This is a hack, we want to be able to run pytest -# without setting environment variables. -os.environ['USAON_VTA_DB_SQLITE'] = 'true' -os.environ['FLASK_DEBUG'] = 'true' PROJECT_DIR = Path(__file__).resolve().parent.parent sys.path.append(PROJECT_DIR) @@ -25,6 +20,7 @@ def typecheck(ctx): print_and_run( f'cd {PROJECT_DIR} &&' f' mypy --config-file={PROJECT_DIR}/.mypy.ini {PACKAGE_DIR}', + env={'USAON_VTA_DB_SQLITE': 'true', 'FLASK_DEBUG': 'true'}, ) print('🎉🦆 Type checking passed.') diff --git a/usaon_vta_survey/__init__.py b/usaon_vta_survey/__init__.py index 49271085..37ae79c6 100644 --- a/usaon_vta_survey/__init__.py +++ b/usaon_vta_survey/__init__.py @@ -1,8 +1,10 @@ import os +import time from typing import Final -from flask import Flask +from flask import Flask, session from flask_bootstrap import Bootstrap5 +from flask_login import LoginManager from flask_sqlalchemy import SQLAlchemy from sqlalchemy import MetaData from sqlalchemy import inspect as sqla_inspect @@ -14,6 +16,9 @@ __version__: Final[str] = VERSION + +# TODO: Figure out where to put this. model.py? +# https://flask.palletsprojects.com/en/2.3.x/patterns/appfactories/#factories-extensions db = SQLAlchemy( metadata=MetaData( naming_convention={ @@ -26,18 +31,66 @@ ) ) -app = Flask(__name__) -app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', 'youcanneverguess') -app.config['LOGIN_DISABLED'] = envvar_is_true("USAON_VTA_LOGIN_DISABLED") -app.config['SQLALCHEMY_DATABASE_URI'] = db_connstr(app) -if envvar_is_true("USAON_VTA_PROXY"): - app.wsgi_app = ProxyFix(app.wsgi_app, x_prefix=1) # type: ignore -db.init_app(app) -bootstrap = Bootstrap5(app) +def create_app(): + """Create and configure the app.""" + # TODO: enable override config to test_config + # https://flask.palletsprojects.com/en/2.3.x/tutorial/factory/ + + app = Flask(__name__) + app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', 'youcanneverguess') + app.config['LOGIN_DISABLED'] = envvar_is_true("USAON_VTA_LOGIN_DISABLED") + app.config['SQLALCHEMY_DATABASE_URI'] = db_connstr(app) + if envvar_is_true("USAON_VTA_PROXY"): + app.wsgi_app = ProxyFix(app.wsgi_app, x_prefix=1) # type: ignore + + db.init_app(app) + Bootstrap5(app) + login_manager = LoginManager() + login_manager.init_app(app) + + from usaon_vta_survey.models.tables import User + + @login_manager.user_loader + def load_user(user_id: str) -> User: + return User.query.get(user_id) + + @app.before_request + def before_request(): + """Handle expired google tokens as a pre-request hook.""" + if token := (s := session).get('google_oauth_token'): + print("Token expiring in", token['expires_at'] - time.time()) + if time.time() >= token['expires_at']: + del s['google_oauth_token'] + + from usaon_vta_survey.routes.google import blueprint + from usaon_vta_survey.routes.login import login_bp + from usaon_vta_survey.routes.logout import logout_bp + from usaon_vta_survey.routes.response import bp + from usaon_vta_survey.routes.response.applications import application_bp + from usaon_vta_survey.routes.response.data_products import dp_bp + from usaon_vta_survey.routes.response.observing_systems import obs_bp + from usaon_vta_survey.routes.response.sbas import sba_bp + from usaon_vta_survey.routes.root import root_bp + from usaon_vta_survey.routes.survey import survey_bp + from usaon_vta_survey.routes.surveys import surveys_bp + from usaon_vta_survey.routes.user import user_bp + from usaon_vta_survey.routes.users import users_bp + + app.register_blueprint(root_bp) + app.register_blueprint(surveys_bp) + app.register_blueprint(survey_bp) + app.register_blueprint(user_bp) + app.register_blueprint(users_bp) + app.register_blueprint(login_bp) + app.register_blueprint(logout_bp) + app.register_blueprint(blueprint, url_prefix="/google_oauth") + app.register_blueprint(bp) + app.register_blueprint(obs_bp) + app.register_blueprint(sba_bp) + app.register_blueprint(application_bp) + app.register_blueprint(dp_bp) -app.jinja_env.globals.update(sqla_inspect=sqla_inspect, __version__=__version__) + app.jinja_env.globals.update(sqla_inspect=sqla_inspect, __version__=__version__) -# NOTE: This is a circular import, but it's specified by the Flask docs: -# https://flask.palletsprojects.com/en/3.1.x/patterns/packages/ -import usaon_vta_survey.routes # noqa: E402, F401 + return app diff --git a/usaon_vta_survey/routes/__init__.py b/usaon_vta_survey/routes/__init__.py deleted file mode 100644 index 88252595..00000000 --- a/usaon_vta_survey/routes/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -import usaon_vta_survey.routes.response -import usaon_vta_survey.routes.response.applications -import usaon_vta_survey.routes.response.data_products -import usaon_vta_survey.routes.response.observing_systems -import usaon_vta_survey.routes.response.sbas -import usaon_vta_survey.routes.response.relationships.application_societal_benefit_area -import usaon_vta_survey.routes.response.relationships.data_product_application -import usaon_vta_survey.routes.response.relationships.observing_system_data_product -import usaon_vta_survey.routes.root -import usaon_vta_survey.routes.login -import usaon_vta_survey.routes.logout -import usaon_vta_survey.routes.user -import usaon_vta_survey.routes.survey -import usaon_vta_survey.routes.surveys -import usaon_vta_survey.routes.users diff --git a/usaon_vta_survey/routes/google.py b/usaon_vta_survey/routes/google.py new file mode 100644 index 00000000..5b3baf5d --- /dev/null +++ b/usaon_vta_survey/routes/google.py @@ -0,0 +1,9 @@ +import os + +from flask_dance.contrib.google import make_google_blueprint + +blueprint = make_google_blueprint( + client_id=os.getenv('USAON_VTA_GOOGLE_CLIENT_ID'), + client_secret=os.getenv('USAON_VTA_GOOGLE_CLIENT_SECRET'), + scope=["profile", "email"], +) diff --git a/usaon_vta_survey/routes/login.py b/usaon_vta_survey/routes/login.py index 5a8474ab..93cdd48b 100644 --- a/usaon_vta_survey/routes/login.py +++ b/usaon_vta_survey/routes/login.py @@ -1,22 +1,13 @@ -import os -import time - -from flask import redirect, session, url_for -from flask_dance.contrib.google import google, make_google_blueprint +from flask import Blueprint, redirect, url_for +from flask_dance.contrib.google import google from flask_login import login_user -from usaon_vta_survey import app from usaon_vta_survey.util.db.user import ensure_user_exists -blueprint = make_google_blueprint( - client_id=os.getenv('USAON_VTA_GOOGLE_CLIENT_ID'), - client_secret=os.getenv('USAON_VTA_GOOGLE_CLIENT_SECRET'), - scope=["profile", "email"], -) -app.register_blueprint(blueprint, url_prefix="/google_oauth") +login_bp = Blueprint('login', __name__, url_prefix='/login') -@app.route("/login") +@login_bp.route("") def login(): if not google.authorized: return redirect(url_for("google.login")) @@ -24,15 +15,17 @@ def login(): assert resp.ok, resp.text user = ensure_user_exists(resp.json()) + # breakpoint() login_user(user) return redirect('/') -@app.before_request -def before_request(): - """Handle expired google tokens as a pre-request hook.""" - if token := (s := session).get('google_oauth_token'): - print("Token expiring in", token['expires_at'] - time.time()) - if time.time() >= token['expires_at']: - del s['google_oauth_token'] +# this may be the login issue +# @app.before_request +# def before_request(): +# """Handle expired google tokens as a pre-request hook.""" +# if token := (s := session).get('google_oauth_token'): +# print("Token expiring in", token['expires_at'] - time.time()) +# if time.time() >= token['expires_at']: +# del s['google_oauth_token'] diff --git a/usaon_vta_survey/routes/logout.py b/usaon_vta_survey/routes/logout.py index 79a43ae5..feb21ce5 100644 --- a/usaon_vta_survey/routes/logout.py +++ b/usaon_vta_survey/routes/logout.py @@ -1,10 +1,10 @@ -from flask import redirect +from flask import Blueprint, redirect from flask_login import logout_user -from usaon_vta_survey import app +logout_bp = Blueprint('logout', __name__, url_prefix='/logout') -@app.route("/logout") +@logout_bp.route("") def logout(): logout_user() return redirect("/") diff --git a/usaon_vta_survey/routes/response/__init__.py b/usaon_vta_survey/routes/response/__init__.py index 66044508..e6837eaa 100644 --- a/usaon_vta_survey/routes/response/__init__.py +++ b/usaon_vta_survey/routes/response/__init__.py @@ -1,11 +1,13 @@ -from flask import render_template +from flask import Blueprint, render_template -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey.models.tables import Response, Survey from usaon_vta_survey.util.authorization import limit_response_editors +bp = Blueprint('response', __name__, url_prefix='/response') -@app.route('/response/', methods=['GET']) + +@bp.route('/', methods=['GET']) def view_response(survey_id: str): """View or create response to a survey.""" # Anyone should be able to view a survey diff --git a/usaon_vta_survey/routes/response/applications.py b/usaon_vta_survey/routes/response/applications.py index 25a4bbaf..82675663 100644 --- a/usaon_vta_survey/routes/response/applications.py +++ b/usaon_vta_survey/routes/response/applications.py @@ -1,13 +1,17 @@ -from flask import redirect, render_template, request, url_for +from flask import Blueprint, redirect, render_template, request, url_for -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey.forms import FORMS_BY_MODEL from usaon_vta_survey.models.tables import ResponseApplication, Survey from usaon_vta_survey.util.authorization import limit_response_editors from usaon_vta_survey.util.sankey import applications_sankey +application_bp = Blueprint( + 'application', __name__, url_prefix='/response//applications' +) -@app.route('/response//applications', methods=['GET', 'POST']) + +@application_bp.route('', methods=['GET', 'POST']) def view_response_applications(survey_id: str): """View and add to applications associated with a response.""" Form = FORMS_BY_MODEL[ResponseApplication] @@ -23,7 +27,9 @@ def view_response_applications(survey_id: str): db.session.add(response_application) db.session.commit() - return redirect(url_for('view_response_applications', survey_id=survey.id)) + return redirect( + url_for('response.view_response_applications', survey_id=survey.id) + ) form = Form(obj=response_application) return render_template( diff --git a/usaon_vta_survey/routes/response/data_products.py b/usaon_vta_survey/routes/response/data_products.py index 93b307e7..bf5310de 100644 --- a/usaon_vta_survey/routes/response/data_products.py +++ b/usaon_vta_survey/routes/response/data_products.py @@ -1,12 +1,16 @@ -from flask import redirect, render_template, request, url_for +from flask import Blueprint, redirect, render_template, request, url_for -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey.forms import FORMS_BY_MODEL from usaon_vta_survey.models.tables import ResponseDataProduct, Survey from usaon_vta_survey.util.authorization import limit_response_editors +dp_bp = Blueprint( + 'data_product', __name__, url_prefix='/response//data_products' +) -@app.route('/response//data_products', methods=['GET', 'POST']) + +@dp_bp.route('', methods=['GET', 'POST']) def view_response_data_products(survey_id: str): """View and add to data products associated with a response.""" Form = FORMS_BY_MODEL[ResponseDataProduct] diff --git a/usaon_vta_survey/routes/response/observing_systems.py b/usaon_vta_survey/routes/response/observing_systems.py index a672838c..d1623481 100644 --- a/usaon_vta_survey/routes/response/observing_systems.py +++ b/usaon_vta_survey/routes/response/observing_systems.py @@ -1,13 +1,17 @@ -from flask import redirect, render_template, request, url_for +from flask import Blueprint, redirect, render_template, request, url_for -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey._types import ObservingSystemType from usaon_vta_survey.forms import FORMS_BY_MODEL from usaon_vta_survey.models.tables import ResponseObservingSystem, Survey from usaon_vta_survey.util.authorization import limit_response_editors +obs_bp = Blueprint( + 'obs', __name__, url_prefix='/response//observing_systems' +) -@app.route('/response//observing_systems', methods=['GET', 'POST']) + +@obs_bp.route('', methods=['GET', 'POST']) def view_response_observing_systems(survey_id: str): """View and add to observing systems associated with a response.""" Form = FORMS_BY_MODEL[ResponseObservingSystem] diff --git a/usaon_vta_survey/routes/response/relationships/application_societal_benefit_area.py b/usaon_vta_survey/routes/response/relationships/application_societal_benefit_area.py index 5f8be7c0..80033c11 100644 --- a/usaon_vta_survey/routes/response/relationships/application_societal_benefit_area.py +++ b/usaon_vta_survey/routes/response/relationships/application_societal_benefit_area.py @@ -2,7 +2,7 @@ from flask_wtf import FlaskForm from wtforms import FormField -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey.forms import FORMS_BY_MODEL from usaon_vta_survey.models.tables import ( ResponseApplication, @@ -10,6 +10,7 @@ ResponseSocietalBenefitArea, Survey, ) +from usaon_vta_survey.routes.response import bp from usaon_vta_survey.util.authorization import limit_response_editors @@ -130,8 +131,8 @@ def _request_args(request: Request) -> tuple[int | None, int | None]: return societal_benefit_area_id, application_id -@app.route( - '/response//application_societal_benefit_area_relationships', +@bp.route( + '//application_societal_benefit_area_relationships', methods=['GET', 'POST'], ) def view_response_application_societal_benefit_area_relationships(survey_id: str): @@ -232,8 +233,8 @@ class SuperForm(FlaskForm): ) -@app.route( - '/response//application_societal_benefit_area_relationships', +@bp.route( + '//application_societal_benefit_area_relationships', methods=['GET', 'POST'], ) def delete_response_application_societal_benefit_area_relationship(survey_id: str): diff --git a/usaon_vta_survey/routes/response/relationships/data_product_application.py b/usaon_vta_survey/routes/response/relationships/data_product_application.py index 6d7259a6..cbbc92d1 100644 --- a/usaon_vta_survey/routes/response/relationships/data_product_application.py +++ b/usaon_vta_survey/routes/response/relationships/data_product_application.py @@ -2,7 +2,7 @@ from flask_wtf import FlaskForm from wtforms import FormField -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey.forms import FORMS_BY_MODEL from usaon_vta_survey.models.tables import ( ResponseApplication, @@ -10,6 +10,7 @@ ResponseDataProductApplication, Survey, ) +from usaon_vta_survey.routes.response import bp from usaon_vta_survey.util.authorization import limit_response_editors @@ -121,8 +122,8 @@ def _request_args(request: Request) -> tuple[int | None, int | None]: return data_product_id, application_id -@app.route( - '/response//data_product_application_relationships', +@bp.route( + '//data_product_application_relationships', methods=['GET', 'POST'], ) def view_response_data_product_application_relationships(survey_id: str): @@ -218,8 +219,8 @@ class SuperForm(FlaskForm): relationship=response_data_product_application, ) - @app.route( - '/response//data_product_application_relationships', + @bp.route( + '//data_product_application_relationships', methods=['GET', 'POST'], ) def delete_response_application_societal_benefit_area_relationship(survey_id: str): diff --git a/usaon_vta_survey/routes/response/relationships/observing_system_data_product.py b/usaon_vta_survey/routes/response/relationships/observing_system_data_product.py index 98855143..299c2c13 100644 --- a/usaon_vta_survey/routes/response/relationships/observing_system_data_product.py +++ b/usaon_vta_survey/routes/response/relationships/observing_system_data_product.py @@ -2,7 +2,7 @@ from flask_wtf import FlaskForm from wtforms import FormField -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey.forms import FORMS_BY_MODEL from usaon_vta_survey.models.tables import ( ResponseDataProduct, @@ -10,6 +10,7 @@ ResponseObservingSystemDataProduct, Survey, ) +from usaon_vta_survey.routes.response import bp from usaon_vta_survey.util.authorization import limit_response_editors @@ -125,8 +126,8 @@ def _request_args(request: Request) -> tuple[int | None, int | None]: return data_product_id, observing_system_id -@app.route( - '/response//observing_system_data_product_relationships', +@bp.route( + '//observing_system_data_product_relationships', methods=['GET', 'POST'], ) def view_response_observing_system_data_product_relationships(survey_id: str): diff --git a/usaon_vta_survey/routes/response/sbas.py b/usaon_vta_survey/routes/response/sbas.py index dc0bbaf0..f83f35e6 100644 --- a/usaon_vta_survey/routes/response/sbas.py +++ b/usaon_vta_survey/routes/response/sbas.py @@ -1,6 +1,6 @@ -from flask import redirect, render_template, request, url_for +from flask import Blueprint, redirect, render_template, request, url_for -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey.forms import FORMS_BY_MODEL from usaon_vta_survey.models.tables import ( ResponseSocietalBenefitArea, @@ -9,10 +9,12 @@ ) from usaon_vta_survey.util.authorization import limit_response_editors - -@app.route( - '/response//societal_benefit_areas', methods=['GET', 'POST'] +sba_bp = Blueprint( + 'sba', __name__, url_prefix='/response//societal_benefit_areas' ) + + +@sba_bp.route('', methods=['GET', 'POST']) def view_response_sbas(survey_id: str): """View and add to observing systems associated with a response.""" sbas = SocietalBenefitArea.query.all() diff --git a/usaon_vta_survey/routes/root.py b/usaon_vta_survey/routes/root.py index 27bd09d9..14b4fdc5 100644 --- a/usaon_vta_survey/routes/root.py +++ b/usaon_vta_survey/routes/root.py @@ -1,9 +1,9 @@ -from flask import render_template +from flask import Blueprint, render_template -from usaon_vta_survey import app +root_bp = Blueprint('root', __name__, url_prefix='/') -@app.route("/") +@root_bp.route('') def root(): return render_template( 'home.html', diff --git a/usaon_vta_survey/routes/survey.py b/usaon_vta_survey/routes/survey.py index 1e6ff28e..505f388d 100644 --- a/usaon_vta_survey/routes/survey.py +++ b/usaon_vta_survey/routes/survey.py @@ -1,11 +1,13 @@ -from flask import redirect, render_template, request, url_for +from flask import Blueprint, redirect, render_template, request, url_for -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey.forms import FORMS_BY_MODEL from usaon_vta_survey.models.tables import Survey +survey_bp = Blueprint('survey', __name__, url_prefix='/survey') -@app.route('/survey/new', methods=['GET', 'POST']) + +@survey_bp.route('/new', methods=['GET', 'POST']) def new_survey(): Form = FORMS_BY_MODEL[Survey] survey = Survey() @@ -19,13 +21,13 @@ def new_survey(): db.session.add(survey) db.session.commit() - return redirect(url_for('view_survey', survey_id=survey.id)) + return redirect(url_for('survey.view_survey', survey_id=survey.id)) form = Form(obj=survey) return render_template('new_survey.html', form=form) -@app.route('/survey/') +@survey_bp.route('/') def view_survey(survey_id: str): # Fetch survey by id survey = db.get_or_404(Survey, survey_id) diff --git a/usaon_vta_survey/routes/surveys.py b/usaon_vta_survey/routes/surveys.py index 62ca975a..844f2867 100644 --- a/usaon_vta_survey/routes/surveys.py +++ b/usaon_vta_survey/routes/surveys.py @@ -1,10 +1,11 @@ -from flask import render_template +from flask import Blueprint, render_template -from usaon_vta_survey import app from usaon_vta_survey.models.tables import Survey +surveys_bp = Blueprint('surveys', __name__, url_prefix='/surveys') -@app.route('/surveys') + +@surveys_bp.route('') def view_surveys(): surveys = Survey.query.order_by(Survey.created_timestamp).all() return render_template( diff --git a/usaon_vta_survey/routes/user.py b/usaon_vta_survey/routes/user.py index 8b63cd3c..d7ba2a19 100644 --- a/usaon_vta_survey/routes/user.py +++ b/usaon_vta_survey/routes/user.py @@ -1,25 +1,18 @@ -from flask import flash, render_template, request -from flask_login import LoginManager, current_user +from flask import Blueprint, flash, render_template, request +from flask_login import current_user -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey.forms import FORMS_BY_MODEL from usaon_vta_survey.models.tables import User -login_manager = LoginManager(app) - - -@login_manager.user_loader -def load_user(user_id: str) -> User: - return User.query.get(user_id) - - -if app.config["LOGIN_DISABLED"]: - # HACK: Always logged in as dev user when login is disabled - import flask_login.utils as flask_login_utils - - from usaon_vta_survey.util.dev import DEV_USER - - flask_login_utils._get_user = lambda: DEV_USER +# NOTE: This is temporary until issue #144 is resolved +# if envvar_is_true("USAON_VTA_LOGIN_DISABLED"): +# # HACK: Always logged in as dev user when login is disabled +# import flask_login.utils as flask_login_utils +# +# from usaon_vta_survey.util.dev import DEV_USER +# +# flask_login_utils._get_user = lambda: DEV_USER def _validate_role_change(user: User, form) -> None: @@ -27,7 +20,10 @@ def _validate_role_change(user: User, form) -> None: raise RuntimeError("Only admins can edit users roles.") -@app.route('/user/', methods=['POST', 'GET']) +user_bp = Blueprint('user', __name__, url_prefix='/user') + + +@user_bp.route('/', methods=['POST', 'GET']) def user(user_id: str): Form = FORMS_BY_MODEL[User] user = db.get_or_404(User, user_id) diff --git a/usaon_vta_survey/routes/users.py b/usaon_vta_survey/routes/users.py index 6f3a18c1..feea2a72 100644 --- a/usaon_vta_survey/routes/users.py +++ b/usaon_vta_survey/routes/users.py @@ -1,10 +1,11 @@ -from flask import render_template +from flask import Blueprint, render_template -from usaon_vta_survey import app from usaon_vta_survey.models.tables import User +users_bp = Blueprint('users', __name__, url_prefix='/users') -@app.route('/users') + +@users_bp.route('') def view_users(): users = User.query.order_by(User.name).all() return render_template( diff --git a/usaon_vta_survey/templates/macros/nav_buttons.j2 b/usaon_vta_survey/templates/macros/nav_buttons.j2 index a724eb63..ef88b008 100644 --- a/usaon_vta_survey/templates/macros/nav_buttons.j2 +++ b/usaon_vta_survey/templates/macros/nav_buttons.j2 @@ -1,18 +1,18 @@ {% macro nav_buttons(current_user) -%} {% from "bootstrap5/nav.html" import render_nav_item %} - {{ render_nav_item("root", "Home")}} - {{ render_nav_item("view_surveys", "Surveys")}} + {{ render_nav_item("root.root", "Home")}} + {{ render_nav_item("surveys.view_surveys", "Surveys")}} {% if current_user.is_authenticated %} - {{ render_nav_item("user", "Profile", user_id=current_user.id)}} + {{ render_nav_item("user.user", "Profile", user_id=current_user.id)}} {% if current_user.role_id=="admin" %} - {{ render_nav_item("view_users", "Users")}} - {{ render_nav_item("logout", "Log out")}} + {{ render_nav_item("users.view_users", "Users")}} + {{ render_nav_item("logout.logout", "Log out")}} {% else %} - {{ render_nav_item("logout", "Log out")}} + {{ render_nav_item("logout.logout", "Log out")}} {% endif %} {% else %} - {{ render_nav_item("login", "Log in")}} + {{ render_nav_item("login.login", "Log in")}} {% endif %} {%- endmacro -%} diff --git a/usaon_vta_survey/templates/response/base.html b/usaon_vta_survey/templates/response/base.html index bf0f1a3a..23da99d7 100644 --- a/usaon_vta_survey/templates/response/base.html +++ b/usaon_vta_survey/templates/response/base.html @@ -9,37 +9,37 @@

{% block title %}Response to: {{survey.title}}{% endblock %}

  • - + Response home
  • - + Observing systems ({{survey.response.observing_systems | length}})
  • - + Data products ({{survey.response.data_products | length}})
  • - + Applications ({{survey.response.applications | length}})
  • - + Societal Benefit Areas ({{survey.response.societal_benefit_areas | length}})
  • - + View survey config
  • diff --git a/usaon_vta_survey/templates/survey.html b/usaon_vta_survey/templates/survey.html index af09c46e..e2d4d640 100644 --- a/usaon_vta_survey/templates/survey.html +++ b/usaon_vta_survey/templates/survey.html @@ -14,13 +14,13 @@

    Notes

    Response

    {% if survey.response_id %} - + View response

    Response by: {{ survey.response.created_by.email}}

    {% else %}

    No response available.

    - + Create response diff --git a/usaon_vta_survey/templates/surveys.html b/usaon_vta_survey/templates/surveys.html index f7bf1dae..b2562bee 100644 --- a/usaon_vta_survey/templates/surveys.html +++ b/usaon_vta_survey/templates/surveys.html @@ -41,7 +41,7 @@

    {% block title %}Surveys{% endblock %}

    {{survey.status.id}} {{survey.private}} - {{render_icon('eye-fill')}} + {{render_icon('eye-fill')}} diff --git a/usaon_vta_survey/templates/users.html b/usaon_vta_survey/templates/users.html index 3ac461ef..54e87f98 100644 --- a/usaon_vta_survey/templates/users.html +++ b/usaon_vta_survey/templates/users.html @@ -7,7 +7,7 @@

    {% block title %}Users{% endblock %}

    {% if not current_user.role_id=='admin' %}

    You don't belong here! This page is for admins only.

    {% else %} - {{ render_table(users, show_actions=True, edit_url=('user', [('user_id',':id')])) }} + {{ render_table(users, show_actions=True, edit_url=('user.user', [('user_id',':id')])) }} {% endif %} diff --git a/usaon_vta_survey/util/db/setup.py b/usaon_vta_survey/util/db/setup.py index 2c3f6e22..4be3105b 100644 --- a/usaon_vta_survey/util/db/setup.py +++ b/usaon_vta_survey/util/db/setup.py @@ -1,8 +1,9 @@ +from flask import current_app from loguru import logger from sqlalchemy import MetaData from sqlalchemy.orm import Session -from usaon_vta_survey import app, db +from usaon_vta_survey import db from usaon_vta_survey.constants.roles import ROLES from usaon_vta_survey.constants.sba import IAOA_SBA_FRAMEWORK from usaon_vta_survey.constants.status import STATUSES @@ -42,7 +43,7 @@ def populate_reference_data() -> None: init_roles(db.session) init_statuses(db.session) - if app.config["LOGIN_DISABLED"]: + if current_app.config["LOGIN_DISABLED"]: init_dev_user(db.session) logger.info('Reference data loaded.')