Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix invoke script using dev DB in non-dev #142

Merged
merged 39 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
53a5c2f
Update script to remove old container
rmarow Sep 14, 2023
9cfae4a
Comment out where environment variables were being set unintentionally
rmarow Sep 15, 2023
ecaa06c
Linting
rmarow Sep 15, 2023
da5d4a7
Add envs to mypy function
rmarow Sep 15, 2023
0e8fc1b
Update envarrs to not be hacky
rmarow Sep 15, 2023
fc8e0d5
part 1 setting up blueprint
rmarow Sep 18, 2023
d018d8e
WIP #2 adding application factory
rmarow Sep 18, 2023
c017dbe
Fix circular import issue
rmarow Sep 18, 2023
ce8dbf6
fix blueprint
rmarow Sep 18, 2023
f29779e
remove unused import
rmarow Sep 18, 2023
3b20204
TEMPORARY resolve `app` errors - need to figure out true solution
rmarow Sep 18, 2023
cca5a3f
WIP different blueprint for responses
rmarow Sep 18, 2023
1b8deea
WIP continue `response` blueprint update
rmarow Sep 18, 2023
82a1891
WIP able to startup app, still getting a blueprint error
rmarow Sep 19, 2023
43913df
Remove prefix from route
rmarow Sep 19, 2023
af2192a
Fix import
rmarow Sep 19, 2023
97b1451
WIP - current blocker is login manager
rmarow Sep 19, 2023
724c533
LoginManager
rmarow Sep 19, 2023
90e69b4
Login manager working
rmarow Sep 19, 2023
e619c6f
Login blueprint
rmarow Sep 19, 2023
471175a
Add survey blueprint and remove `app` from user.py
rmarow Sep 19, 2023
f018cbc
Update task/db to use create_app function
rmarow Sep 19, 2023
e6304f6
Import login_bp
rmarow Sep 19, 2023
f929ee9
Attempt at getting login working - not successful
rmarow Sep 20, 2023
e9e563f
Temporarily remove ability to bypass googleSSO
rmarow Sep 20, 2023
5a7ebfc
Remove unused `app` in this file
rmarow Sep 20, 2023
92e2738
Fix login related error and call current_app as recommended by docs
rmarow Sep 21, 2023
25241bd
Add logout blueprint
rmarow Sep 21, 2023
d7c50cb
Update users template to include blueprint
rmarow Sep 21, 2023
53a5177
Add survey blueprint to redirect url
rmarow Sep 21, 2023
376c23e
Add response blueprint
rmarow Sep 21, 2023
ac3fcf0
Start adding response blueprint - response sub pages need their own bp
rmarow Sep 21, 2023
1b1e101
view surveys blueprint
rmarow Sep 21, 2023
af5ca07
remove breakpoint
rmarow Sep 25, 2023
af48db0
Add comments regarding issue #144
rmarow Sep 25, 2023
714e8bf
Merge branch 'non-dev-db-issue' of github.com:nsidc/usaon-vta-survey …
rmarow Sep 25, 2023
4a3113a
Group blueprint imports together to reduce additional spacing
rmarow Sep 25, 2023
1e52cd0
Update path
rmarow Sep 28, 2023
b9bd638
Adding blueprints
rmarow Sep 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
rmarow marked this conversation as resolved.
Show resolved Hide resolved
- "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
Expand Down
2 changes: 1 addition & 1 deletion scripts/invoke_in_container.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 6 additions & 2 deletions tasks/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -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...')
Expand All @@ -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()
6 changes: 1 addition & 5 deletions tasks/test.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import os
import sys
from pathlib import Path

from invoke import task

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)

Expand All @@ -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.')

Expand Down
79 changes: 66 additions & 13 deletions usaon_vta_survey/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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={
Expand All @@ -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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rmarow bp here is hard to read. What about response_bp?

I feel obs_bp and dp_bp would also be more readable spelling out the words.

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
15 changes: 0 additions & 15 deletions usaon_vta_survey/routes/__init__.py

This file was deleted.

9 changes: 9 additions & 0 deletions usaon_vta_survey/routes/google.py
Original file line number Diff line number Diff line change
@@ -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"],
)
33 changes: 13 additions & 20 deletions usaon_vta_survey/routes/login.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,31 @@
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"))
resp = google.get("/oauth2/v2/userinfo")
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']
6 changes: 3 additions & 3 deletions usaon_vta_survey/routes/logout.py
Original file line number Diff line number Diff line change
@@ -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("/")
8 changes: 5 additions & 3 deletions usaon_vta_survey/routes/response/__init__.py
Original file line number Diff line number Diff line change
@@ -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/<string:survey_id>', methods=['GET'])

@bp.route('/<string:survey_id>', methods=['GET'])
def view_response(survey_id: str):
"""View or create response to a survey."""
# Anyone should be able to view a survey
Expand Down
14 changes: 10 additions & 4 deletions usaon_vta_survey/routes/response/applications.py
Original file line number Diff line number Diff line change
@@ -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/<string:survey_id>/applications'
)

@app.route('/response/<string:survey_id>/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]
Expand All @@ -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(
Expand Down
10 changes: 7 additions & 3 deletions usaon_vta_survey/routes/response/data_products.py
Original file line number Diff line number Diff line change
@@ -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/<string:survey_id>/data_products'
)

@app.route('/response/<string:survey_id>/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]
Expand Down
10 changes: 7 additions & 3 deletions usaon_vta_survey/routes/response/observing_systems.py
Original file line number Diff line number Diff line change
@@ -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/<string:survey_id>/observing_systems'
)

@app.route('/response/<string:survey_id>/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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
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,
ResponseApplicationSocietalBenefitArea,
ResponseSocietalBenefitArea,
Survey,
)
from usaon_vta_survey.routes.response import bp
from usaon_vta_survey.util.authorization import limit_response_editors


Expand Down Expand Up @@ -130,8 +131,8 @@ def _request_args(request: Request) -> tuple[int | None, int | None]:
return societal_benefit_area_id, application_id


@app.route(
'/response/<string:survey_id>/application_societal_benefit_area_relationships',
@bp.route(
'/<string:survey_id>/application_societal_benefit_area_relationships',
methods=['GET', 'POST'],
)
def view_response_application_societal_benefit_area_relationships(survey_id: str):
Expand Down Expand Up @@ -232,8 +233,8 @@ class SuperForm(FlaskForm):
)


@app.route(
'/response/<string:survey_id>/application_societal_benefit_area_relationships',
@bp.route(
'/<string:survey_id>/application_societal_benefit_area_relationships',
methods=['GET', 'POST'],
)
def delete_response_application_societal_benefit_area_relationship(survey_id: str):
Expand Down
Loading