Skip to content

Commit

Permalink
Merge 1ea942d into bdd587d
Browse files Browse the repository at this point in the history
  • Loading branch information
armish committed Sep 3, 2015
2 parents bdd587d + 1ea942d commit c9c6ff4
Show file tree
Hide file tree
Showing 28 changed files with 148 additions and 89 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 @@
"""Start associating comments with registered users and use user information
as a display name instead of arbitrary author names.
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()),)
44 changes: 35 additions & 9 deletions cycledash/api/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
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

from common.helpers import tables, to_epoch
from cycledash import db
from cycledash.helpers import (prepare_request_data, success_response,
abort_if_none_for, camelcase_dict)
abort_if_none_for, camelcase_dict,
desentisize_user)
from cycledash.validations import Doc, to_epoch

from . import Resource, validate_with, marshal_with
Expand All @@ -20,8 +22,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 +31,7 @@

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

CommentFields = Schema({
Expand All @@ -52,8 +52,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 @@ -77,9 +77,11 @@ def get(self, run_id):
@marshal_with(CommentFields)
def post(self, run_id):
"""Create a comment."""
print current_user
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 @@ -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 = userify_comment(comment)
results_map[row_key].append(comment)
return {'comments': results_map}

Expand All @@ -174,8 +177,30 @@ 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 userify_comments(epochify_comments(comments))

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

def userify_comment(comment):
"""Given a comment with userId, attaches the relevant user
info to the comment
"""
if 'userId' in comment:
with tables(db.engine, 'users') as (con, users):
q = select(users.c).where(users.c.id == comment['userId'])
user = q.execute().fetchone()
if user is not None:
user = desentisize_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 +230,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))
18 changes: 18 additions & 0 deletions cycledash/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,24 @@ def is_anonymous(self):
def get_id(self):
return unicode(self['id'])

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

def is_authenticated(self):
return False

def is_active(self):
return False

def is_anonymous(self):
return True

def get_id(self):
return self['id']

login_manager.anonymous_user = AnonymousUser

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

def desentisize_user(user):
"""Removes sensitive user information before passing it to the frontend."""
del user['password']
del user['email']
return user

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

0 comments on commit c9c6ff4

Please sign in to comment.