Skip to content

Commit

Permalink
Merge pull request #831 from hammerlab/comments-with-users
Browse files Browse the repository at this point in the history
Associate comments with users and use username for author name
  • Loading branch information
armish committed Sep 14, 2015
2 parents 96e24c0 + 536595e commit 40db63e
Show file tree
Hide file tree
Showing 29 changed files with 161 additions and 93 deletions.
28 changes: 28 additions & 0 deletions alembic/versions/1ab5aa1e3054_start_associating_comments_with_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Adds user_id column to the user_comments and drops the author_name column.
This allows us to associate comments with users in the database.
Revision ID: 1ab5aa1e3054
Revises: 2c00861f3ff1
Create Date: 2015-09-01 18:05:35.598253
"""

# revision identifiers, used by Alembic.
revision = '1ab5aa1e3054'
down_revision = '2c00861f3ff1'
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa

def upgrade():
op.drop_column('user_comments', 'author_name')
op.add_column('user_comments',
sa.Column('user_id',
sa.BigInteger,
sa.ForeignKey('users.id')))

def downgrade():
op.drop_column('user_comments', 'user_id')
op.add_column('user_comments', sa.Column('author_name', sa.String()),)
43 changes: 35 additions & 8 deletions cycledash/api/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from collections import defaultdict
from flask import jsonify, request
from flask.ext.restful import abort, fields
from flask.ext.login import current_user
from sqlalchemy import select, func, desc
from voluptuous import Any, Required, Coerce, Schema

Expand All @@ -20,8 +21,7 @@
Required("position"): Coerce(int),
Required("reference"): basestring,
Required("alternates"): basestring,
Required("comment_text"): basestring,
"author_name": basestring,
Required("comment_text"): basestring
})

DeleteComment = Schema({
Expand All @@ -30,8 +30,7 @@

UpdateComment = Schema({
Required('last_modified'): Coerce(float),
"comment_text": basestring,
"author_name": basestring,
"comment_text": basestring
})

CommentFields = Schema({
Expand All @@ -52,8 +51,8 @@
basestring,
Doc('comment_text', 'The text of the comment.'):
Any(basestring, None),
Doc('author_name', 'The name of the author of this comment.'):
Any(basestring, None),
Doc('user_id', 'The ID of the User this comment is associated with.'):
Any(long, None),
Doc('created',
'The time at which the comment was created (in epoch time).'):
Coerce(to_epoch),
Expand All @@ -80,6 +79,7 @@ def post(self, run_id):
with tables(db.engine, 'user_comments') as (con, comments):
q = comments.insert().values(
vcf_id=run_id,
user_id=current_user['id'],
**request.validated_body
).returning(*comments.c)
return dict(q.execute().fetchone()), 201
Expand Down Expand Up @@ -148,6 +148,8 @@ def get_vcf_comments(vcf_id):
"<row_key STRING>": [...], ...
}
}
where comments have an additional "user" field.
"""
def _row_key(comment, table):
cols = ['contig', 'position', 'reference', 'alternates', 'sample_name']
Expand All @@ -163,6 +165,7 @@ def _row_key(comment, table):
comment['last_modified'] = to_epoch(comment['last_modified'])
comment['created'] = to_epoch(comment['created'])
comment = camelcase_dict(comment)
comment = add_user_to_comment(comment)
results_map[row_key].append(comment)
return {'comments': results_map}

Expand All @@ -174,8 +177,31 @@ def get_last_comments(n=5):
q = select(cols.values()).order_by(
desc(cols.created)).limit(n)
comments = [camelcase_dict(dict(c)) for c in con.execute(q).fetchall()]
return epochify_comments(comments)
return add_user_to_comments(epochify_comments(comments))

def add_user_to_comments(comments):
"""Given comments with userIds, attaches the relevant user
info to the comments.
"""
for comment in comments:
add_user_to_comment(comment)
return comments

def add_user_to_comment(comment):
"""Given a comment with userId, attaches the relevant user
information (user: id, username) to the comment (comment: user).
"""
if 'userId' in comment:
with tables(db.engine, 'users') as (con, users):
q = select([users.c.username, users.c.id]).where(
users.c.id == comment['userId'])
user = q.execute().fetchone()
if user:
user = dict(user)
comment['user'] = user
else:
comment['user'] = None
return comment

def epochify_comments(comments):
"""Sets `lastModified` and `created` to be epoch time instead of iso8061."""
Expand Down Expand Up @@ -205,4 +231,5 @@ def _get_comment(comment_table, id=None, **query_kwargs):
q = comment_table.select().where(comment_table.c.id == id)
for colname, val in query_kwargs.items():
q = q.where(comment_table.c[colname] == val)
return dict(abort_if_none_for('comment')(q.execute().fetchone(), id))
comment = q.execute().fetchone()
return dict(abort_if_none_for('comment')(comment, id))
15 changes: 15 additions & 0 deletions cycledash/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ def is_anonymous(self):
def get_id(self):
return unicode(self['id'])

class AnonymousUser(User):
def __init__(self):
self['id'] = None
self['username'] = u'Anonymous'

def is_active(self):
return False

def is_anonymous(self):
return True

def is_authenticated(self):
return False

login_manager.anonymous_user = AnonymousUser

def wrap_user(user):
"""Wraps user record returned from the database in a class providing methods
Expand Down
1 change: 0 additions & 1 deletion cycledash/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,5 @@ def abort_if_none(obj, obj_id):
return obj
return abort_if_none


class CollisionError(Exception):
pass
3 changes: 1 addition & 2 deletions cycledash/static/js/comments/components/comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ var Comment = React.createClass({
// moment uses the local timezone by default (converting the
// value, which starts as a UNIX timestamp, to that timezone)
var relativeDate = moment.unix(comment.created).fromNow();
var authorName = comment.authorName ?
comment.authorName.slice(0, 15) : 'Anonymous';
var authorName = comment.userId ? comment.user.username : "Anonymous";
return (
<li>
<div className='author-name'>{authorName}</div>
Expand Down
Loading

0 comments on commit 40db63e

Please sign in to comment.