-
Notifications
You must be signed in to change notification settings - Fork 1
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
Db connection issue #100
base: master
Are you sure you want to change the base?
Db connection issue #100
Conversation
webapp/query.py
Outdated
|
||
def get_model_parameters(model_id): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected 2 blank lines, found 1
webapp/controller.py
Outdated
methods=['GET', 'POST']) | ||
def get_model_detail(model_id): | ||
df = query.get_model_parameters(model_id) | ||
output=df |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing whitespace around operator
webapp/controller.py
Outdated
@@ -395,7 +392,16 @@ def get_metric_over_time(model_id): | |||
except Exception as e: | |||
return jsonify({"sorry": "Sorry, no results! Please try again."}), 500 | |||
|
|||
|
|||
@app.route( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected 2 blank lines, found 1
webapp/controller.py
Outdated
|
||
# Default DB | ||
app.config['DB_NAME'] = "cmpd" | ||
from webapp import db |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'webapp.db' imported but unused
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you're not really done with this. I was just curious about what was going on in Tyra-land 😁
webapp/__init__.py
Outdated
|
||
app = Flask(__name__,instance_relative_config=True) | ||
app.secret_key = "tyra american top models" | ||
app.config['SQLALCHEMY_DATABASE_URI'] = db_dict['sfpd']['url'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure, but is it possible to omit this configuration setting? It seems like the project doesn't really have a "default" or "main" database connection, (unless we are in fact using SFPD that way?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good call.
@@ -3,9 +3,15 @@ | |||
from flask_sqlalchemy import SQLAlchemy | |||
import flask_login | |||
import json | |||
from config import db_dict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is me getting out of scope, but I'm seeing a lot of code in our projects to map environment and/or file configuration variables to database URIs. The standard I've used in the past is simply to pass database URIs around, in a single setting variable. For frameworks which don't have native support for this URI, there are often simple libraries to add support; and, it's cool that Flask-SQLAlchemy supports these natively.
DATABASE_URI=postgresql://USER@HOST:PORT/DATABASE?PARAMS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I absolutely agree with you that the config thing is kinda messy or redundant. I don't like that personally too. Maybe we should change it in other PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
webapp/controller.py
Outdated
@@ -43,7 +41,6 @@ def testing(): | |||
@app.route('/db_choose/<string:project>', methods=['GET', 'POST']) | |||
def db_choose(project): | |||
try: | |||
app.config['SQLALCHEMY_DATABASE_URI'] = db_dict[project]['url'] | |||
app.config['DB_NAME'] = project |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a ton of experience with Flask, and so I'm curious – is this app.config
dictionary not process-global? Is db_choose
meant for something other than for the user to select the database for their session?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I end up using flask-session now.
session['engine'] = project
webapp/query.py
Outdated
return model_comments | ||
|
||
|
||
def get_model_groups(query_arg): | ||
engine = db.get_engine(app, app.config['DB_NAME']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it might not make more sense for the caller (the controller) to pass the database engine to the query method, rather than ask the query method to look up the appropriate database in this shared configuration? At least, that would make these query methods more easily testable. And it makes sense for the controller to be tying the parts together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I change to pass the database engine to the query method.
In controller.py:
engine = db.get_engine(app, session['engine'])
query_arg = {'model_id': model_id,
'evaluation_start_time': evaluation_start_time}
prediction_df = query.get_model_prediction(query_arg, engine)
webapp/query.py
Outdated
df = pd.read_sql( | ||
query, | ||
con=engine) | ||
print(df) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stray print
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
terminated!
webapp/query.py
Outdated
FROM results.models | ||
WHERE model_id={model_id}; | ||
""".format( | ||
model_id=model_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we're not too worried about SQL injection attacks here, but I'd suggest passing expression parameters to the database driver, nonetheless. At the least it might make this a little cleaner.
return pd.read_sql("""\
SELECT model_parameters
FROM results.models
WHERE model_id=%s
""",
engine,
params=(model_id,),
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah for model_id
and as_of_date
we should pass it to database driver ( I don't know why I miss this one and I'll fix it), but for column_name
or schema_name
it won't work and have to just interpolate.
webapp/query.py
Outdated
query = """ | ||
SELECT feature_list FROM results.model_groups WHERE model_group_id={} | ||
""".format(model_group_id) | ||
df = pd.read_sql( | ||
query, | ||
con=db.engine) | ||
con=engine) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined name 'engine'
webapp/query.py
Outdated
) | ||
output = df_importance | ||
return output | ||
|
||
def get_feature_importance(query_arg): | ||
def get_feature_importance(query_arg, engine): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected 2 blank lines, found 1
webapp/query.py
Outdated
@@ -1,12 +1,11 @@ | |||
import pandas as pd | |||
from webapp import db | |||
from webapp import app |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'webapp.app' imported but unused
webapp/__init__.py
Outdated
@@ -1,12 +1,26 @@ | |||
from flask import Flask | |||
from flask import Flask, session | |||
from flask.ext.session import Session, SqlAlchemySessionInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'flask.ext.session.SqlAlchemySessionInterface' imported but unused
webapp/__init__.py
Outdated
@@ -1,12 +1,26 @@ | |||
from flask import Flask | |||
from flask import Flask, session |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'flask.session' imported but unused
Codecov Report
@@ Coverage Diff @@
## master #100 +/- ##
=========================================
- Coverage 71.77% 71.17% -0.6%
=========================================
Files 4 4
Lines 418 444 +26
=========================================
+ Hits 300 316 +16
- Misses 118 128 +10
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🆒 – just a couple comments
app.secret_key = "tyra american top models" | ||
app.config['SQLALCHEMY_DATABASE_URI'] = db_dict['cmpd']['url'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you intend to keep this setting? It looks like you've just switched defaulting to SFPD with CMPD.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh and in case this setting is required, that is that Flask requires a default database connection, that's OK, of course; though, I might suggest we make it something that doesn't actually resolve, to avoid unintentionally defaulting.
Moreover, we might want a database user/schema for Tyra, e.g. for session data, which this could (at some point) point to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(bump)
app.secret_key = "tyra american top models" | ||
app.config['SQLALCHEMY_DATABASE_URI'] = db_dict['cmpd']['url'] | ||
|
||
app.config['SQLALCHEMY_BINDS'] = {key: db_dict[key]['url'] for key in db_dict.keys()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing, but if you like 😸
{key: value['url'] for (key, value) in db_dict.items()}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point.
db = SQLAlchemy(app) | ||
|
||
app.config['SESSION_TYPE'] = 'filesystem' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do we currently deploy Tyra? Just want to check that it has a filesystem appropriate for writing session data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's on an ec2 machine.
@@ -11,8 +11,6 @@ | |||
import os | |||
from config import db_dict | |||
|
|||
# Default DB | |||
app.config['DB_NAME'] = "cmpd" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to set/get a default database in the session?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope. removed!
# try: | ||
# engine = db.get_engine(app, app.config['DB_NAME']) | ||
# except: | ||
# engine = db.engine |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stale code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(bump)
@@ -21,6 +21,8 @@ | |||
|
|||
def test_individual_importances(): | |||
with rig_test_client(data) as test_app: | |||
with test_app.session_transaction() as session: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we just want to add an optional engine to rig_test_client
and have it handle this?
assert expected == response_data | ||
|
||
def test_get_feature_dist_train(): | ||
with rig_test_client(data) as test_app: | ||
with test_app.session_transaction() as session: | ||
session['engine'] = 'cmpd' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what exactly is happening in these cases where you only set the session engine in the context-managed block? usually a context manager is doing something in __enter__
and something else in __exit__
. does this affect the remainder of the test? is it different from:
session = test_app.session_transaction()
session['engine'] = 'cmpd'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will give me this error if I don't use the context-managed block with session.
TypeError: '_GeneratorContextManager' object does not support item assignment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense 😄 I would've guessed that it had an interface that wasn't based around the context manager, but I'm not sure it matters, anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, in fact, I would guess that it's writing the session to the backend upon __exit__
😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Just a couple comments, hopefully helpful.
assert expected == response_data | ||
|
||
def test_get_feature_dist_train(): | ||
with rig_test_client(data) as test_app: | ||
with test_app.session_transaction() as session: | ||
session['engine'] = 'cmpd' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, in fact, I would guess that it's writing the session to the backend upon __exit__
😉
app.secret_key = "tyra american top models" | ||
app.config['SQLALCHEMY_DATABASE_URI'] = db_dict['cmpd']['url'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(bump)
# try: | ||
# engine = db.get_engine(app, app.config['DB_NAME']) | ||
# except: | ||
# engine = db.engine |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(bump)
try: | ||
return jsonify(results=output) | ||
except Exception as e: | ||
return jsonify({"sorry": "Sorry, no results! Please try again."}), 500 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible for jsonify
to fail here? For that matter, is there no middleware or other layer which catches Python exceptions and returns response 500
, (such as in flask
or gunicorn
)? It seems as though there's always a chance of an unanticipated, and therefore uncaught, exception being raised; but, here, we're not catching a particular case and returning a useful message, so much as (re?)-implementing a default exception handler.
app.config['SQLALCHEMY_DATABASE_URI']
andapp.config['SQLALCHEMY_BINDS']