Skip to content

Commit

Permalink
Merge pull request #662 from neuroscout/enh/pc_fk
Browse files Browse the repository at this point in the history
Add direct fk from Predictor Predictor Collection + User predictors route
  • Loading branch information
adelavega committed Sep 18, 2019
2 parents 132c391 + 537c8d8 commit 9e78e4d
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 24 deletions.
1 change: 1 addition & 0 deletions neuroscout/core.py
Expand Up @@ -53,6 +53,7 @@
('UserTriggerResetResource', 'user/reset_password'),
('UserResetSubmitResource', 'user/submit_token'),
('UserResendConfirm', 'user/resend_confirmation'),
('UserPredictorListResource', 'user/predictors'),
('TaskResource', 'tasks/<int:task_id>'),
('TaskListResource', 'tasks')
])
Expand Down
13 changes: 4 additions & 9 deletions neuroscout/models/predictor.py
Expand Up @@ -18,6 +18,9 @@ class Predictor(db.Model):

predictor_events = db.relationship('PredictorEvent', backref='predictor')

predictor_collection_id = db.Column(
db.Integer, db.ForeignKey('predictor_collection.id'))

predictor_run = db.relationship('PredictorRun')
active = db.Column(db.Boolean, default=True) # Actively display or not
private = db.Column(db.Boolean, default=False)
Expand Down Expand Up @@ -53,19 +56,11 @@ class PredictorRun(db.Model):
primary_key=True)


# Association table between collection and predictor.
collection_predictor = db.Table(
'collection_predictor',
db.Column('pc_id', db.Integer(), db.ForeignKey('predictor_collection.id')),
db.Column('predictor_id', db.Integer(), db.ForeignKey('predictor.id')))


class PredictorCollection(db.Model):
""" Predictor Collection Upload """
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
predictors = db.relationship('Predictor', secondary=collection_predictor,
backref='predictor_collection')
predictors = db.relationship('Predictor', backref='predictor_collection')

uploaded_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
collection_name = db.Column(db.Text, nullable=False)
Expand Down
4 changes: 3 additions & 1 deletion neuroscout/resources/__init__.py
Expand Up @@ -11,7 +11,8 @@
PredictorCollectionResource, prepare_upload)
from .run import RunResource, RunListResource
from .user import (UserRootResource, UserTriggerResetResource,
UserResetSubmitResource, UserResendConfirm)
UserResetSubmitResource, UserResendConfirm,
UserPredictorListResource)
from .task import TaskResource, TaskListResource

__all__ = [
Expand All @@ -37,6 +38,7 @@
'UserTriggerResetResource',
'UserResetSubmitResource',
'UserResendConfirm',
'UserPredictorListResource',
'TaskResource',
'TaskListResource',
'prepare_upload'
Expand Down
17 changes: 12 additions & 5 deletions neuroscout/resources/predictor.py
Expand Up @@ -23,7 +23,7 @@ def get(self, predictor_id, **kwargs):
return first_or_404(Predictor.query.filter_by(id=predictor_id))


def get_predictors(newest=True, **kwargs):
def get_predictors(newest=True, user=None, **kwargs):
""" Helper function for querying newest predictors """
if newest:
predictor_ids = db.session.query(
Expand All @@ -36,14 +36,21 @@ def get_predictors(newest=True, **kwargs):
predictor_ids = predictor_ids.join(PredictorRun).filter(
PredictorRun.run_id.in_(kwargs.pop('run_id')))

query = Predictor.query.filter(Predictor.id.in_(predictor_ids)).filter_by(
private=False
)
query = Predictor.query.filter(Predictor.id.in_(predictor_ids))

for param in kwargs:
query = query.filter(getattr(Predictor, param).in_(kwargs[param]))

query = query.filter_by(active=True)

if user is not None:
query = query.filter_by(private=True).join(
PredictorCollection).filter_by(user_id=user.id)
else:
query = query.filter_by(private=False)

# Only display active predictors
return query.filter_by(active=True).all()
return query.all()


class PredictorListResource(MethodResource):
Expand Down
34 changes: 25 additions & 9 deletions neuroscout/resources/user.py
@@ -1,5 +1,6 @@
from flask_jwt import current_identity, jwt_required
from flask_security.recoverable import reset_password_token_status
import webargs as wa

from flask_apispec import MethodResource, marshal_with, use_kwargs, doc
from ..models.auth import User
Expand All @@ -8,21 +9,21 @@
from .utils import abort, auth_required
from ..utils.db import put_record
from ..schemas.user import UserSchema, UserCreationSchema, UserResetSchema
from ..schemas.predictor import PredictorSchema
from .predictor import get_predictors


# @doc(tags=['auth'])
@marshal_with(UserSchema)
class UserRootResource(MethodResource):
@doc(summary='Get current user information.')
@doc(tags=['user'], summary='Get current user information.')
@auth_required
def get(self):
return current_identity

@doc(summary='Add a new user.')
@use_kwargs(UserCreationSchema)
def post(self, **kwargs):
return register_user(**kwargs)

@doc(summary='Edit user information.')
@use_kwargs(UserSchema)
@auth_required
def put(self, **kwargs):
Expand All @@ -32,12 +33,7 @@ def put(self, **kwargs):
return put_record(kwargs, current_identity)


# @doc(tags=['auth'])
class UserResendConfirm(MethodResource):
@doc(summary='Resend confirmation email.')
@doc(params={"authorization": {
"in": "header", "required": True,
"description": "Format: JWT {authorization_token}"}})
@jwt_required()
def post(self):
if send_confirmation(current_identity):
Expand Down Expand Up @@ -66,3 +62,23 @@ def post(self, **kwargs):
user.password = kwargs['password']
db.session.commit()
return {'message': 'Password reset succesfully.'}


class UserPredictorListResource(MethodResource):
@doc(tags=['user'], summary='Get list of user predictors.',)
@use_kwargs({
'run_id': wa.fields.DelimitedList(
wa.fields.Int(), description="Run id(s). Warning, slow query."),
'name': wa.fields.DelimitedList(wa.fields.Str(),
description="Predictor name(s)"),
'newest': wa.fields.Boolean(
missing=True,
description="Return only newest Predictor by name")
},
locations=['query'])
@auth_required
@marshal_with(PredictorSchema(many=True))
def get(self, **kwargs):
newest = kwargs.pop('newest')
return get_predictors(newest=newest, user=current_identity,
**kwargs)
1 change: 1 addition & 0 deletions neuroscout/tasks/upload.py
Expand Up @@ -66,6 +66,7 @@ def upload_collection(flask_app, filenames, runs, dataset_id, collection_id,
name=col,
source=f'Collection: {collection_object.collection_name}',
dataset_id=dataset_id,
predictor_collection_id=collection_object.id,
private=True,
description=descriptions.get(col))
db.session.add(predictor)
Expand Down
4 changes: 4 additions & 0 deletions neuroscout/tests/api/test_predictor.py
Expand Up @@ -133,3 +133,7 @@ def test_predictor_create(session,
assert resp['source'] == 'Collection: new_one'
assert resp['name'] == 'trial_type'
assert resp['description'] == 'new_description'

# Test user PC route:
resp = decode_json(auth_client.get('/api/user/predictors'))
assert len(resp) == 3
33 changes: 33 additions & 0 deletions postgres/migrations/migrations/versions/3fedbc6e3973_.py
@@ -0,0 +1,33 @@
"""empty message
Revision ID: 3fedbc6e3973
Revises: 5cf42e00634a
Create Date: 2019-09-13 21:17:41.075663
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '3fedbc6e3973'
down_revision = '5cf42e00634a'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('collection_predictor')
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('collection_predictor',
sa.Column('pc_id', sa.INTEGER(), autoincrement=False, nullable=True),
sa.Column('predictor_id', sa.INTEGER(), autoincrement=False, nullable=True),
sa.ForeignKeyConstraint(['pc_id'], ['predictor_collection.id'], name='collection_predictor_pc_id_fkey'),
sa.ForeignKeyConstraint(['predictor_id'], ['predictor.id'], name='collection_predictor_predictor_id_fkey')
)
# ### end Alembic commands ###
30 changes: 30 additions & 0 deletions postgres/migrations/migrations/versions/5cf42e00634a_.py
@@ -0,0 +1,30 @@
"""empty message
Revision ID: 5cf42e00634a
Revises: 8dedbfca852e
Create Date: 2019-09-13 21:14:08.084208
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '5cf42e00634a'
down_revision = '8dedbfca852e'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('predictor', sa.Column('predictor_collection_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'predictor', 'predictor_collection', ['predictor_collection_id'], ['id'])
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'predictor', type_='foreignkey')
op.drop_column('predictor', 'predictor_collection_id')
# ### end Alembic commands ###

0 comments on commit 9e78e4d

Please sign in to comment.