From 1667cfc7b1cfe11cd9547eaa6f0f7c9776086b3e Mon Sep 17 00:00:00 2001 From: skarakash Date: Mon, 8 Sep 2025 14:07:29 +0300 Subject: [PATCH] added analytics service to track product page visits --- Dockerfile | 2 +- src/catalogue/views/product.py | 4 ++++ src/common/databases/mongo_db.py | 3 ++- src/reviews/models/mongo.py | 8 ++++++++ src/reviews/repositories.py | 5 ++++- src/reviews/services.py | 16 +++++++++++++++- 6 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index c1410224..9fda0358 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/src/catalogue/views/product.py b/src/catalogue/views/product.py index 5b05453d..6cba1d35 100644 --- a/src/catalogue/views/product.py +++ b/src/catalogue/views/product.py @@ -18,6 +18,7 @@ from src.catalogue.services import get_product_service from src.common.exceptions.base import ObjectDoesNotExistException from src.common.schemas.common import ErrorResponse +from src.reviews.services import ProductAnalyticsService router = APIRouter(prefix=CatalogueRoutesPrefixes.product) @@ -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. @@ -60,6 +62,8 @@ async def product_detail( """ try: response = await service.detail(pk=pk) + # track product visit + await analytics_service.track_product_visit(product_id=pk) except ObjectDoesNotExistException as exc: response.status_code = status.HTTP_404_NOT_FOUND return ErrorResponse(message=exc.message) diff --git a/src/common/databases/mongo_db.py b/src/common/databases/mongo_db.py index 6667f098..52b59adc 100644 --- a/src/common/databases/mongo_db.py +++ b/src/common/databases/mongo_db.py @@ -6,7 +6,7 @@ from src.base_settings import base_settings from src.common.singleton import SingletonMeta -from src.reviews.models.mongo import ProductReview +from src.reviews.models.mongo import ProductReview, ProductAnalytics class AsyncMongoDBClient(AsyncIOMotorClient, metaclass=SingletonMeta): @@ -21,5 +21,6 @@ async def init_mongo_db(): database=client.get_database(), document_models=[ ProductReview, + ProductAnalytics ], ) diff --git a/src/reviews/models/mongo.py b/src/reviews/models/mongo.py index f19b77ff..6dc5ee63 100644 --- a/src/reviews/models/mongo.py +++ b/src/reviews/models/mongo.py @@ -1,5 +1,6 @@ import uuid from typing import Optional +from datetime import datetime from beanie import Document from pydantic import ( @@ -28,3 +29,10 @@ class ProductReview(Document, BaseProductReview): class Settings: name = 'productReviews' + +class ProductAnalytics(Document): + product_id: int + timestamp: datetime = Field(default_factory=datetime.now) + + class Settings: + name = 'productAnalytics' \ No newline at end of file diff --git a/src/reviews/repositories.py b/src/reviews/repositories.py index 9a96d08c..f2c9355f 100644 --- a/src/reviews/repositories.py +++ b/src/reviews/repositories.py @@ -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 \ No newline at end of file diff --git a/src/reviews/services.py b/src/reviews/services.py index 13afdef1..50f19972 100644 --- a/src/reviews/services.py +++ b/src/reviews/services.py @@ -7,8 +7,9 @@ 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): @@ -44,3 +45,16 @@ 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 track_product_visit(self, product_id: int) -> ProductAnalytics: + """Track a product page visit by creating an analytics record.""" + analytics_record = ProductAnalytics(product_id=product_id) + return await self.repository.create(analytics_record)