feat: implement Instagram clone SocialPhoto API
- FastAPI backend with SQLite database - JWT authentication (register, login) - User profiles with follow/unfollow - Posts with image upload and likes/dislikes - Comments with likes - Global and personalized feed - Comprehensive pytest test suite (37 tests) TASK-ID: 758f4029-702
This commit is contained in:
124
app/routes/feed.py
Normal file
124
app/routes/feed.py
Normal file
@@ -0,0 +1,124 @@
|
||||
"""Feed routes for SocialPhoto."""
|
||||
import sqlite3
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
|
||||
from app.auth import get_current_user_id
|
||||
from app.database import get_db, row_to_dict
|
||||
from app.models import FeedResponse, PostResponse
|
||||
|
||||
router = APIRouter(prefix="/feed", tags=["Feed"])
|
||||
security = HTTPBearer()
|
||||
|
||||
|
||||
@router.get("", response_model=FeedResponse)
|
||||
async def get_followed_feed(
|
||||
limit: int = Query(default=20, ge=1, le=100),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
credentials: HTTPAuthorizationCredentials = Depends(security),
|
||||
conn: sqlite3.Connection = Depends(get_db),
|
||||
) -> FeedResponse:
|
||||
"""Get feed of posts from users you follow."""
|
||||
user_id = await get_current_user_id(credentials)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Get posts from followed users
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT
|
||||
p.id, p.user_id, u.username, p.image_path, p.caption, p.created_at,
|
||||
(SELECT COUNT(*) FROM likes WHERE post_id = p.id) as likes_count,
|
||||
(SELECT COUNT(*) FROM dislikes WHERE post_id = p.id) as dislikes_count,
|
||||
(SELECT COUNT(*) FROM comments WHERE post_id = p.id) as comments_count
|
||||
FROM posts p
|
||||
JOIN users u ON p.user_id = u.id
|
||||
WHERE p.user_id IN (
|
||||
SELECT following_id FROM follows WHERE follower_id = ?
|
||||
)
|
||||
ORDER BY p.created_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
""",
|
||||
(user_id, limit, offset),
|
||||
)
|
||||
|
||||
posts = []
|
||||
for row in cursor.fetchall():
|
||||
post = row_to_dict(row)
|
||||
posts.append(
|
||||
PostResponse(
|
||||
id=post["id"],
|
||||
user_id=post["user_id"],
|
||||
username=post["username"],
|
||||
image_url=f"/uploads/{post['image_path'].split('/')[-1]}",
|
||||
caption=post["caption"],
|
||||
likes_count=post["likes_count"],
|
||||
dislikes_count=post["dislikes_count"],
|
||||
comments_count=post["comments_count"],
|
||||
created_at=post["created_at"],
|
||||
)
|
||||
)
|
||||
|
||||
# Get total count
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT COUNT(*) as total
|
||||
FROM posts p
|
||||
WHERE p.user_id IN (
|
||||
SELECT following_id FROM follows WHERE follower_id = ?
|
||||
)
|
||||
""",
|
||||
(user_id,),
|
||||
)
|
||||
total = cursor.fetchone()["total"]
|
||||
|
||||
return FeedResponse(posts=posts, total=total, limit=limit, offset=offset)
|
||||
|
||||
|
||||
@router.get("/global", response_model=FeedResponse)
|
||||
async def get_global_feed(
|
||||
limit: int = Query(default=20, ge=1, le=100),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
conn: sqlite3.Connection = Depends(get_db),
|
||||
) -> FeedResponse:
|
||||
"""Get global feed of all posts."""
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT
|
||||
p.id, p.user_id, u.username, p.image_path, p.caption, p.created_at,
|
||||
(SELECT COUNT(*) FROM likes WHERE post_id = p.id) as likes_count,
|
||||
(SELECT COUNT(*) FROM dislikes WHERE post_id = p.id) as dislikes_count,
|
||||
(SELECT COUNT(*) FROM comments WHERE post_id = p.id) as comments_count
|
||||
FROM posts p
|
||||
JOIN users u ON p.user_id = u.id
|
||||
ORDER BY p.created_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
""",
|
||||
(limit, offset),
|
||||
)
|
||||
|
||||
posts = []
|
||||
for row in cursor.fetchall():
|
||||
post = row_to_dict(row)
|
||||
posts.append(
|
||||
PostResponse(
|
||||
id=post["id"],
|
||||
user_id=post["user_id"],
|
||||
username=post["username"],
|
||||
image_url=f"/uploads/{post['image_path'].split('/')[-1]}",
|
||||
caption=post["caption"],
|
||||
likes_count=post["likes_count"],
|
||||
dislikes_count=post["dislikes_count"],
|
||||
comments_count=post["comments_count"],
|
||||
created_at=post["created_at"],
|
||||
)
|
||||
)
|
||||
|
||||
# Get total count
|
||||
cursor.execute("SELECT COUNT(*) as total FROM posts")
|
||||
total = cursor.fetchone()["total"]
|
||||
|
||||
return FeedResponse(posts=posts, total=total, limit=limit, offset=offset)
|
||||
Reference in New Issue
Block a user