Skip to content

Commit

Permalink
Feature/354 comment (#384)
Browse files Browse the repository at this point in the history
* added model for comment

* api for like/dislike comment

* add permission check for update and delete

* downgrade to pyjwt==1.7.1

Co-authored-by: yus252 <[email protected]>
  • Loading branch information
vinovo and yus252 authored Jan 26, 2021
1 parent f5ae21a commit d673943
Show file tree
Hide file tree
Showing 11 changed files with 372 additions and 2 deletions.
1 change: 1 addition & 0 deletions backend/alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from users import models as user_models
from NytLiveCounty import models as nyt_models
from likes import models as like_models
from comments import models as comment_model

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""add table for comment like
Revision ID: 4e16c97d06e7
Revises: 78b6d95e9727
Create Date: 2021-01-16 11:51:46.777209
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "4e16c97d06e7"
down_revision = "78b6d95e9727"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"comment_likes",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("updated_at", sa.DateTime(), nullable=True),
sa.Column("like", sa.Boolean(), nullable=True),
sa.Column("comment_id", sa.Integer(), nullable=True),
sa.Column("story_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["comment_id"], ["comments.id"],),
sa.ForeignKeyConstraint(["story_id"], ["stories.id"],),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_comment_likes_id"), "comment_likes", ["id"], unique=False
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_comment_likes_id"), table_name="comment_likes")
op.drop_table("comment_likes")
# ### end Alembic commands ###
43 changes: 43 additions & 0 deletions backend/alembic/versions/78b6d95e9727_add_comment_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""add comment model
Revision ID: 78b6d95e9727
Revises: edace0efb31c
Create Date: 2021-01-16 10:31:42.706758
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "78b6d95e9727"
down_revision = "edace0efb31c"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"comments",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("updated_at", sa.DateTime(), nullable=True),
sa.Column("text", sa.Text(), nullable=True),
sa.Column("parent", sa.Integer(), nullable=True),
sa.Column("story_id", sa.Integer(), nullable=True),
sa.Column("my_story_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["my_story_id"], ["my_stories.id"],),
sa.ForeignKeyConstraint(["parent"], ["comments.id"],),
sa.ForeignKeyConstraint(["story_id"], ["stories.id"],),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_comments_id"), "comments", ["id"], unique=False)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_comments_id"), table_name="comments")
op.drop_table("comments")
# ### end Alembic commands ###
Empty file added backend/comments/__init__.py
Empty file.
97 changes: 97 additions & 0 deletions backend/comments/crud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import and_

from . import models, schemas
from stories.crud import update


def create_comment(db: Session, comment: schemas.CommentCreate):
db_comment = models.Comment(**comment.dict())
db.add(db_comment)
db.commit()
db.refresh(db_comment)

return db_comment


def get_comments_by_my_story(db: Session, my_story_id):
return (
db.query(models.Comment)
.filter(models.Comment.my_story_id == my_story_id)
.all()
)


def update_comment(db: Session, comment_id, comment: schemas.CommentUpdate):
return update(comment_id, comment, models.Comment, db)


def delete_comment(db: Session, comment_id):
db.query(models.Comment).filter(models.Comment.id == comment_id).delete()
db.commit()


def get_comment(db: Session, comment_id):
db_comment = (
db.query(models.Comment)
.filter(models.Comment.id == comment_id)
.first()
)

return db_comment


def get_like_by_comment_and_user(db: Session, comment_id, story_id):
return (
db.query(models.CommentLike)
.filter(
and_(
models.CommentLike.comment_id == comment_id,
models.CommentLike.story_id == story_id,
)
)
.first()
)


def like_comment(db: Session, comment_id, story_id, is_like):
like = schemas.CommentLike(
like=is_like, comment_id=comment_id, story_id=story_id
)

db_like = get_like_by_comment_and_user(db, comment_id, story_id)
if db_like:
update(db_like.id, like, models.CommentLike, db)
else:
db_like = models.CommentLike(**like.dict())
db.add(db_like)
db.commit()

db.refresh(db_like)
return db_like


def count_like(db: Session, comment_id):
like = (
db.query(models.CommentLike)
.filter(
and_(
models.CommentLike.comment_id == comment_id,
models.CommentLike.like == 1,
)
)
.count()
)

dislike = (
db.query(models.CommentLike)
.filter(
and_(
models.CommentLike.comment_id == comment_id,
models.CommentLike.like == 0,
)
)
.count()
)

return {"like": like, "dislike": dislike}
23 changes: 23 additions & 0 deletions backend/comments/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from sqlalchemy import Column, Text, ForeignKey, Integer, Boolean
from database import Base
from sqlalchemy.orm import relationship


class Comment(Base):
__tablename__ = "comments"

text = Column(Text)
parent = Column(Integer, ForeignKey("comments.id"))
story_id = Column(Integer, ForeignKey("stories.id"))
my_story_id = Column(Integer, ForeignKey("my_stories.id"))

story = relationship("Story", back_populates="comments")
my_story = relationship("MyStory", back_populates="comments")


class CommentLike(Base):
__tablename__ = "comment_likes"

like = Column(Boolean)
comment_id = Column(Integer, ForeignKey("comments.id"))
story_id = Column(Integer, ForeignKey("stories.id"))
38 changes: 38 additions & 0 deletions backend/comments/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from datetime import date
from pydantic import BaseModel
from stories.schemas import Story


class CommentUpdate(BaseModel):
text: str


class CommentBase(CommentUpdate):
parent: int = None


class CommentCreate(CommentBase):
story_id: int
my_story_id: int


class Comment(CommentCreate):
id: int
updated_at: date
created_at: date
story: Story = None

class Config:
orm_mode = True


class CommentLikeCreate(BaseModel):
like: bool = None


class CommentLike(CommentLikeCreate):
comment_id: int
story_id: int

class Config:
orm_mode = True
13 changes: 12 additions & 1 deletion backend/router/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
from fastapi import APIRouter

from router import auth, stories, users, symptoms, data, nyt_live_county, likes
from router import (
auth,
stories,
users,
symptoms,
data,
nyt_live_county,
likes,
comments,
)

router = APIRouter()

Expand All @@ -19,3 +28,5 @@
)

router.include_router(likes.router, prefix="/likes", tags=["likes"])

router.include_router(comments.router, prefix="/comments")
108 changes: 108 additions & 0 deletions backend/router/comments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from starlette.responses import JSONResponse

from auth import main
from stories import schemas as stories_schemas
from database import get_db
from comments import crud, schemas
from router.stories import check_permissions

router = APIRouter()


@router.post("/my_stories/{my_story_id}", response_model=schemas.Comment)
def create_comment(
my_story_id: int,
comment: schemas.CommentBase,
current_story: stories_schemas.Story = Depends(main.get_current_story),
db: Session = Depends(get_db),
):
if not current_story:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User must first share their story before commenting",
headers={"WWW-Authenticate": "Bearer"},
)

story_id = current_story.id
comment = schemas.CommentCreate(
story_id=story_id, my_story_id=my_story_id, **comment.dict()
)
comment.story_id = story_id
comment.my_story_id = my_story_id

return crud.create_comment(db, comment)


@router.get("/my_stories/{my_story_id}", response_model=List[schemas.Comment])
def get_comments_by_my_story(my_story_id: int, db: Session = Depends(get_db)):
return crud.get_comments_by_my_story(db, my_story_id)


@router.post("/{comment_id}", response_model=schemas.Comment)
def update_comment(
comment_id: int,
comment: schemas.CommentUpdate,
current_story: stories_schemas.Story = Depends(main.get_current_story),
db: Session = Depends(get_db),
):
db_comment = crud.get_comment(db, comment_id)
check_permissions(current_story, db_comment.story_id)
return crud.update_comment(db, comment_id, comment)


@router.delete("/{comment_id}")
def delete_comment(
comment_id: int,
current_story: stories_schemas.Story = Depends(main.get_current_story),
db: Session = Depends(get_db),
):
db_comment = crud.get_comment(db, comment_id)
check_permissions(current_story, db_comment.story_id)
crud.delete_comment(db, comment_id)


@router.get("/{comment_id}", response_model=schemas.Comment)
def get_comment(
comment_id: int, db: Session = Depends(get_db),
):
return crud.get_comment(db, comment_id)


@router.post("/{comment_id}/like", response_model=schemas.CommentLike)
def like_comment(
comment_id: int,
dto: schemas.CommentLikeCreate,
current_story: stories_schemas.Story = Depends(main.get_current_story),
db: Session = Depends(get_db),
):
if not current_story:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User must first share their story before "
+ "liking or dislike a comment",
headers={"WWW-Authenticate": "Bearer"},
)

return crud.like_comment(db, comment_id, current_story.id, dto.like)


@router.get("/{comment_id}/like")
def count_like(
comment_id: int,
current_story: stories_schemas.Story = Depends(main.get_current_story),
db: Session = Depends(get_db),
):
d = crud.count_like(db, comment_id)
db_like = (
crud.get_like_by_comment_and_user(db, comment_id, current_story.id)
if current_story
else None
)

like_by_me = db_like.like if db_like else None
d["like_by_me"] = like_by_me

return JSONResponse(d, status_code=200,)
Loading

0 comments on commit d673943

Please sign in to comment.