Skip to content

Commit

Permalink
WIP: Add appreciations API
Browse files Browse the repository at this point in the history
  • Loading branch information
recrsn committed Feb 13, 2021
1 parent 6b25869 commit 2f5659e
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 13 deletions.
9 changes: 8 additions & 1 deletion snowflake/acl/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
from .appreciations import can_create_appreciations, can_view_appreciations, can_view_appreciation
from .one_on_one import can_view_one_on_one, can_delete_one_on_one

__all__ = ['can_view_one_on_one', 'can_delete_one_on_one']
__all__ = [
'can_view_one_on_one',
'can_delete_one_on_one',
'can_view_appreciations',
'can_view_appreciation',
'can_create_appreciations'
]
13 changes: 13 additions & 0 deletions snowflake/acl/appreciations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from ..models import Appreciation


def can_view_appreciations():
return True


def can_create_appreciations():
return True


def can_view_appreciation(_: Appreciation):
return True
1 change: 1 addition & 0 deletions snowflake/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
app.register_blueprint(api.healthcheck.blueprint, url_prefix="/api/healthcheck")
app.register_blueprint(index.blueprint)

app.register_blueprint(api.appreciations.blueprint, url_prefix="/api/appreciations")
app.register_blueprint(api.notifications.blueprint, url_prefix="/api/notifications")
app.register_blueprint(api.one_on_ones.blueprint, url_prefix="/api/one_on_ones")
app.register_blueprint(api.token.blueprint, url_prefix="/api/tokens")
Expand Down
1 change: 1 addition & 0 deletions snowflake/controllers/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from . import appreciations
from . import healthcheck
from . import notifications
from . import one_on_ones
Expand Down
142 changes: 142 additions & 0 deletions snowflake/controllers/api/appreciations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import re
from datetime import datetime

from flask import Blueprint, request
from flask_login import login_required, current_user

from .response import bad_request, not_found, unauthorized
from ... import acl
from ...models import Appreciation, Like, User, Mention
from ...schemas.appreciation import AppreciationSchema, CreateAppreciationSchema
from ...services import notification

blueprint = Blueprint('api.appreciations', __name__)

appreciation_schema = AppreciationSchema()
create_appreciation_schema = CreateAppreciationSchema()


def get_appreciation_viewer_like(appreciation: Appreciation):
return Like.get_by_appreciation_and_user(appreciation, current_user)


def appreciation_view(appreciation: Appreciation):
return {
'id': appreciation.id,
'content': appreciation.content,
'created_at': appreciation.created_at,
'created_by': appreciation.created_by,
'like_count': appreciation.like_count,
'comment_count': appreciation.comment_count,
'mentions': appreciation.mentions,

'viewer_like': get_appreciation_viewer_like(appreciation),
}


@login_required
@blueprint.route('', methods=['GET'])
def list_all_appreciations():
if not acl.can_view_appreciations():
return unauthorized()

formatted_appreciations = [appreciation_view(a) for a in Appreciation.get_all()]
return appreciation_schema.jsonify(formatted_appreciations, many=True)


@login_required
@blueprint.route('', methods=['PUT'])
def create_appreciation():
if not request.is_json:
return bad_request()

if not acl.can_create_appreciations():
return unauthorized()

appreciation: Appreciation = create_appreciation_schema.load(request.json)

appreciation.created_by = current_user
appreciation.created_at = datetime.now()

Appreciation.create(appreciation)

mentions = re.findall(r'@[a-zA-Z0-9._]+', appreciation.content)

for mention_text in mentions:
user = User.get_by_username(mention_text[1:])
if user is None:
continue
mention = Mention(user=user, appreciation=appreciation)
Mention.create(mention)

notification.notify_appreciation(appreciation)

return appreciation_schema.jsonify(appreciation)


@login_required
@blueprint.route('/<_id>', methods=['GET'])
def get_appreciation(_id):
appreciation = Appreciation.get(_id)

if not appreciation:
return not_found()

if not acl.can_view_appreciation(appreciation):
return unauthorized()

return appreciation_schema.jsonify(appreciation)


@login_required
@blueprint.route('/<_id>', methods=['PATCH'])
def update_appreciation(_id):
return not_found()


@login_required
@blueprint.route('/<_id>', methods=['DELETE'])
def delete_appreciation(_id):
pass


@login_required
@blueprint.route('/<appreciation_id>/likes', methods=['GET'])
def get_appreciation_likes(appreciation_id): # pylint: disable=unused-argument
pass


@login_required
@blueprint.route('/<appreciation_id>/likes', methods=['PUT'])
def like(appreciation_id): # pylint: disable=unused-argument
pass


@login_required
@blueprint.route('/<appreciation_id>/likes/<like_id>', methods=['DELETE'])
def delete_like(appreciation_id, like_id): # pylint: disable=unused-argument
pass


@login_required
@blueprint.route('/<appreciation_id>/comments', methods=['GET'])
def get_comments(appreciation_id): # pylint: disable=unused-argument
pass


@login_required
@blueprint.route('/<appreciation_id>/comments', methods=['GET'])
def create_comment(appreciation_id): # pylint: disable=unused-argument
pass


@login_required
@blueprint.route('/<appreciation_id>/comments/<comment_id>', methods=['GET'])
def update_comment(appreciation_id, comment_id): # pylint: disable=unused-argument
pass


@login_required
@blueprint.route('/<appreciation_id>/comments/<comment_id>', methods=['GET'])
def delete_comment(appreciation_id, comment_id): # pylint: disable=unused-argument
pass
22 changes: 15 additions & 7 deletions snowflake/models/appreciation.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import datetime

from .comment import Comment
from .like import Like
from .user import User
from ..db import db
Expand All @@ -21,6 +22,14 @@ class Appreciation(db.Model):
def creator(self):
return self.created_by

@property
def like_count(self):
return Like.query.filter_by(appreciation=self).count()

@property
def comment_count(self):
return Comment.query.filter_by(appreciation=self).count()

@staticmethod
def create(appreciation):
db.session.add(appreciation)
Expand All @@ -31,11 +40,10 @@ def get_all():
return Appreciation.query.order_by(Appreciation.created_at.desc()).all()

def get_like_count(self):
return Like.query.filter_by(appreciation=self).count()
return self.like_count

def get_comment_count(self):
return db.session.scalar('SELECT COUNT(*) FROM comment c WHERE c.appreciation_id = :id',
{'id': self.id})
return self.comment_count

def is_liked_by(self, user: User):
return db.session.scalar(
Expand All @@ -46,12 +54,15 @@ def is_liked_by(self, user: User):
{'appreciation_id': self.id, 'user_id': user.id}) > 0

@staticmethod
def get(id_):
def get(id_) -> 'Appreciation':
return Appreciation.query.get(id_)

def get_mentions(self):
return self.mentions

def get_comments(self):
return self.comments

@staticmethod
def count_by_user(user: User):
return Appreciation.query.filter_by(created_by=user).count()
Expand Down Expand Up @@ -80,6 +91,3 @@ def most_appreciated():
})

return result

def get_comments(self):
return self.comments
3 changes: 1 addition & 2 deletions snowflake/models/comment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from datetime import datetime

from .appreciation import Appreciation
from ..db import db


Expand All @@ -13,7 +12,7 @@ class Comment(db.Model):
created_by = db.relationship('User')

appreciation_id = db.Column(db.BigInteger, db.ForeignKey('appreciation.id'), nullable=False)
appreciation: Appreciation = db.relationship('Appreciation')
appreciation = db.relationship('Appreciation')

@staticmethod
def create(comment):
Expand Down
10 changes: 9 additions & 1 deletion snowflake/models/like.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from datetime import datetime
from typing import TYPE_CHECKING

from ..db import db

if TYPE_CHECKING:
from . import Appreciation


class Like(db.Model):
id = db.Column(db.BigInteger, primary_key=True)
Expand All @@ -12,7 +16,7 @@ class Like(db.Model):
created_by = db.relationship('User', backref=db.backref('likes', lazy=True))

appreciation_id = db.Column(db.String, db.ForeignKey('appreciation.id'), nullable=False)
appreciation = db.relationship('Appreciation')
appreciation: 'Appreciation' = db.relationship('Appreciation')

@staticmethod
def create(like):
Expand All @@ -28,3 +32,7 @@ def dislike(appreciation, user):
like = Like.query.filter_by(appreciation_id=appreciation.id, user_id=user.id).first()
db.session.delete(like)
db.session.commit()

@staticmethod
def get_by_appreciation_and_user(appreciation, user):
return Like.query.filter_by(appreciation=appreciation, user=user).first()
41 changes: 41 additions & 0 deletions snowflake/schemas/appreciation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from marshmallow.fields import Integer, String, DateTime, List
from marshmallow_sqlalchemy.fields import Nested

from .base import BaseSQLAlchemySchema, BaseSchema
from ..marshmallow import marshmallow
from ..models import Like
from ..schemas.user import UserSchema


class LikeSchema(BaseSQLAlchemySchema):
class Meta:
model = Like

id = marshmallow.auto_field()
created_by = Nested(UserSchema)


class MentionSchema(BaseSchema):
user = Nested(UserSchema)


class AppreciationSchema(BaseSchema):
id = Integer()
content = String()
created_at = DateTime()

created_by = Nested(UserSchema)

like_count = Integer()
comment_count = Integer()
viewer_like = Nested(LikeSchema, exclude=("user",))

mentions = List(Nested(MentionSchema))


class CreateAppreciationSchema(BaseSchema):
class Meta:
model = True
load_instance = True

content = String()
4 changes: 2 additions & 2 deletions snowflake/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
class="clear-button has-text-danger level-item is-clickable"
data-toggle-modal="#likes-{{ appreciation.id }}">
<span>
{% set like_count = appreciation.get_like_count() %}
{% set like_count = appreciation.like_count %}
{{ like_count }} {{ choose_plural(like_count, 'like', 'likes') }}
</span>
</button>
Expand All @@ -103,7 +103,7 @@
<span class="icon is-medium">
<ion-icon name="chatbubble-outline"></ion-icon>
</span>
<span>{{ appreciation.get_comment_count() }}</span>
<span>{{ appreciation.comment_count }}</span>
</button>
<div class="modal" id="likes-{{ appreciation.id }}">
<div class="modal-background"></div>
Expand Down
Loading

0 comments on commit 2f5659e

Please sign in to comment.