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
10 changes: 5 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ WORKDIR /app

COPY pyproject.toml pyproject.toml

RUN pip install poetry
RUN poetry config virtualenvs.create false
RUN poetry install --no-dev
RUN pip install --no-cache-dir poetry \
&& poetry config virtualenvs.create false

RUN poetry install --no-root --no-interaction --no-ansi

COPY . /app

RUN chmod +x ops/start-api.sh


CMD ["sh", "ops/start-api.sh"]
CMD ["sh", "ops/start-api.sh"]
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.8'

services:
web:
build: .
Expand Down
Empty file added src/analytics/__init__.py
Empty file.
20 changes: 20 additions & 0 deletions src/analytics/models/mongo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from datetime import datetime, timezone

from beanie import Document
from pydantic import BaseModel


class BaseProductAnalytics(BaseModel):
product_id: int
timestamp: datetime


class ProductAnalytics(Document, BaseProductAnalytics):
class Settings:
name = 'productAnalytics'

@classmethod
def new_visit(cls, product_id: int, timestamp: datetime | None = None) -> "ProductAnalytics":
if timestamp is None:
timestamp = datetime.now(timezone.utc)
return cls(product_id=product_id, timestamp=timestamp)
6 changes: 6 additions & 0 deletions src/analytics/repositories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from src.common.repository.beanie import BaseMongoRepository
from src.analytics.models.mongo import ProductAnalytics


class ProductAnalyticsRepository(BaseMongoRepository[ProductAnalytics]):
__model__ = ProductAnalytics
22 changes: 22 additions & 0 deletions src/analytics/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from datetime import datetime, timezone
from typing import Annotated, Optional

from fastapi import Depends

from src.common.service import BaseService
from src.analytics.models.mongo import ProductAnalytics
from src.analytics.repositories import ProductAnalyticsRepository


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

async def record_visit(self, product_id: int, timestamp: Optional[datetime] = None) -> ProductAnalytics:
if timestamp is None:
timestamp = datetime.now(timezone.utc)
instance = ProductAnalytics(product_id=product_id, timestamp=timestamp)
return await self.repository.create(instance=instance)
3 changes: 3 additions & 0 deletions src/catalogue/views/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
ProductRoutesPrefixes,
)
from src.catalogue.services import get_product_service
from src.analytics.services import ProductAnalyticsService
from src.common.exceptions.base import ObjectDoesNotExistException
from src.common.schemas.common import ErrorResponse

Expand Down Expand Up @@ -51,6 +52,7 @@ async def product_detail(
response: Response,
pk: int,
service: Annotated[get_product_service, Depends()],
analytics_service: Annotated[ProductAnalyticsService, Depends()],
) -> Union[Response, ErrorResponse]:
"""
Retrieve product.
Expand All @@ -60,6 +62,7 @@ async def product_detail(
"""
try:
response = await service.detail(pk=pk)
await analytics_service.record_visit(product_id=pk)
except ObjectDoesNotExistException as exc:
response.status_code = status.HTTP_404_NOT_FOUND
return ErrorResponse(message=exc.message)
Expand Down
2 changes: 2 additions & 0 deletions src/common/databases/mongo_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from src.base_settings import base_settings
from src.common.singleton import SingletonMeta
from src.reviews.models.mongo import ProductReview
from src.analytics.models.mongo import ProductAnalytics


class AsyncMongoDBClient(AsyncIOMotorClient, metaclass=SingletonMeta):
Expand All @@ -21,5 +22,6 @@ async def init_mongo_db():
database=client.get_database(),
document_models=[
ProductReview,
ProductAnalytics,
],
)