Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ COPY pyproject.toml pyproject.toml

RUN pip install poetry
RUN poetry config virtualenvs.create false
RUN poetry install --no-dev
RUN poetry install --no-root

COPY . /app

Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ services:
- mongo-db
networks:
- web_network
env_file:
- env.example

mongo-db:
image: mongo:latest
Expand Down
3 changes: 3 additions & 0 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ POSTGRES_DB=fastapi_shop

API_KEY=some-api-key
AUTH__SECRET_KEY=09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7
AUTH__ALGORITHM=HS256
AUTH__ACCESS_TOKEN_EXPIRE_MINUTES=30
AUTH__CRYPT_SCHEMA=bcrypt
8 changes: 8 additions & 0 deletions src/reviews/models/mongo.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import uuid
from typing import Optional
from datetime import datetime

from beanie import Document
from pydantic import (
Expand Down Expand Up @@ -28,3 +29,10 @@ class ProductReview(Document, BaseProductReview):

class Settings:
name = 'productReviews'

class ProductAnalytics(Document):
product_id: int
timestamp: datetime = Field(default_factory=datetime.utcnow)

class Settings:
name = "product_analytics"
5 changes: 4 additions & 1 deletion src/reviews/repositories.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from src.common.repository.beanie import BaseMongoRepository
from src.reviews.models.mongo import ProductReview
from src.reviews.models.mongo import ProductReview, ProductAnalytics


class ProductReviewRepository(BaseMongoRepository[ProductReview]):
__model__ = ProductReview

class ProductAnalyticsRepository(BaseMongoRepository[ProductAnalytics]):
__model__ = ProductAnalytics
16 changes: 15 additions & 1 deletion src/reviews/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

from fastapi import Depends

from datetime import datetime

from src.common.exceptions.base import ObjectDoesNotExistException
from src.common.service import BaseService
from src.reviews.models.mongo import (
ProductReview,
Reply,
ProductAnalytics,
)
from src.reviews.repositories import ProductReviewRepository
from src.reviews.repositories import ProductReviewRepository, ProductAnalyticsRepository


class ProductReviewService(BaseService):
Expand Down Expand Up @@ -44,3 +47,14 @@ async def add_reply(self, pk: str, reply: Reply) -> ProductReview:
review.replies.append(reply.model_dump())

return await review.save()

class ProductAnalyticsService(BaseService):
def __init__(self, repository: Annotated[ProductAnalyticsRepository, Depends(ProductAnalyticsRepository)]):
super().__init__(repository=repository)

async def log_visit(self, product_id: int) -> ProductAnalytics:
visit = ProductAnalytics(
product_id=product_id,
timestamp=datetime.utcnow()
)
return await self.repository.create(visit)
31 changes: 30 additions & 1 deletion src/reviews/views/product_reviews.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Depends,
Response,
status,
HTTPException,
)

from src.common.exceptions.base import ObjectDoesNotExistException
Expand All @@ -21,7 +22,8 @@
ProductReviewRoutesPrefixes,
ReviewsRoutesPrefixes,
)
from src.reviews.services import ProductReviewService
from src.reviews.services import ProductReviewService, ProductAnalyticsService
from src.reviews.repositories import ProductAnalyticsRepository, ProductReviewRepository


router = APIRouter(prefix=ReviewsRoutesPrefixes.product_reviews)
Expand Down Expand Up @@ -113,3 +115,30 @@ async def add_reply_to_review(
return ErrorResponse(message=exc.message)

return response

def get_review_service() -> ProductReviewService:
return ProductReviewService(ProductReviewRepository())


def get_analytics_service() -> ProductAnalyticsService:
return ProductAnalyticsService(ProductAnalyticsRepository())


@router.get("/{product_id}")
async def product_detail(
product_id: int,
review_service: Annotated[ProductReviewService, Depends(get_review_service)],
analytics_service: Annotated[ProductAnalyticsService, Depends(get_analytics_service)],
):
try:
product: ProductReview = await review_service.repository.get(pk=str(product_id))
except ObjectDoesNotExistException as exc:
raise HTTPException(status_code=404, detail=exc.message)

await analytics_service.log_visit(product_id)

return {
"id": str(product.id),
"title": product.title,
"description": product.description,
}