Skip to content

Commit

Permalink
Merge pull request #142 from nsidc/non-dev-db-issue
Browse files Browse the repository at this point in the history
Fix invoke script using dev DB in non-dev
  • Loading branch information
rmarow authored Sep 28, 2023
2 parents 23a34df + b9bd638 commit 46bc4ed
Show file tree
Hide file tree
Showing 28 changed files with 208 additions and 142 deletions.
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:
- "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)
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

0 comments on commit 46bc4ed

Please sign in to comment.