Skip to content

Commit

Permalink
Merge pull request #825 from Nifilat/feat/get-analytics-summary
Browse files Browse the repository at this point in the history
fix: updated dashboard statistics to use standard route
  • Loading branch information
joboy-dev authored Aug 8, 2024
2 parents c8b7d3e + 28f2bd9 commit 506e2cb
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 97 deletions.
2 changes: 0 additions & 2 deletions api/v1/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
from api.v1.routes.privacy import privacies
from api.v1.routes.settings import settings
from api.v1.routes.terms_and_conditions import terms_and_conditions
from api.v1.routes.statistics import statistics

api_version_one = APIRouter(prefix="/api/v1")

Expand Down Expand Up @@ -89,4 +88,3 @@
api_version_one.include_router(team)
api_version_one.include_router(terms_and_conditions)
api_version_one.include_router(product_comment)
api_version_one.include_router(statistics)
44 changes: 40 additions & 4 deletions api/v1/routes/dashboard.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, status
from api.db.database import get_db
from sqlalchemy.orm import Session

Expand All @@ -11,11 +11,24 @@
DashboardSingleProductResponse,
DashboardProductListResponse
)
from typing import Annotated
from fastapi.security import OAuth2
from datetime import datetime, timedelta
from api.v1.services.user import oauth2_scheme
from api.v1.services.analytics import analytics_service, AnalyticsServices


dashboard = APIRouter(prefix="/dashboard", tags=['Dashboard'])


def get_current_month_date_range():
now = datetime.utcnow()
start_date = datetime(now.year, now.month, 1)
end_date = (start_date + timedelta(days=32)
).replace(day=1) - timedelta(seconds=1)
return start_date, end_date


@dashboard.get("/products/count", response_model=DashboardProductCountResponse)
async def get_products_count(
db: Session = Depends(get_db),
Expand All @@ -28,7 +41,7 @@ async def get_products_count(
message="Products count fetched successfully",
data={"count": len(products)}
)


@dashboard.get("/products", response_model=DashboardProductListResponse)
async def get_products(
Expand Down Expand Up @@ -56,7 +69,7 @@ async def get_products(
message="Products fetched successfully",
data=payment_data
)


@dashboard.get("/products/{product_id}", response_model=DashboardSingleProductResponse)
async def get_product(
Expand All @@ -79,4 +92,27 @@ async def get_product(
"archived": prod.archived,
"created_at": prod.created_at.isoformat(),
}
)
)


@dashboard.get('/statistics', status_code=status.HTTP_200_OK)
async def get_analytics_summary(
token: Annotated[str, Depends(oauth2_scheme)],
db: Annotated[Session, Depends(get_db)],
analytics_service: Annotated[AnalyticsServices, Depends()],
start_date: datetime = None,
end_date: datetime = None
):
"""
Retrieves analytics summary data for an organization or super admin.
Args:
token: access_token
db: database Session object
start_date: start date for filtering
end_date: end date for filtering
Returns:
analytics response: contains the analytics summary data
"""
if not start_date or not end_date:
start_date, end_date = get_current_month_date_range()
return analytics_service.get_analytics_summary(token=token, db=db, start_date=start_date, end_date=end_date)
45 changes: 0 additions & 45 deletions api/v1/routes/statistics.py

This file was deleted.

33 changes: 25 additions & 8 deletions api/v1/schemas/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,35 @@ class AnalyticsChartsResponse(BaseModel):
data: Dict


class Metrics(BaseModel):
"""
Base schema for metrics with current and previous month values and percentage difference.
"""
current_month: Union[float, int]
previous_month: Union[float, int]
percentage_difference: str


class ActiveUsersMetrics(BaseModel):
"""
Schema for active users metrics with the current value and the difference from an hour ago.
"""
current: int
difference_an_hour_ago: int


class SuperAdminMetrics(BaseModel):
total_revenue: Dict[str, Union[float, str]]
total_products: Dict[str, Union[int, str]]
total_users: Dict[str, Union[int, str]]
lifetime_sales: Dict[str, Union[float, str]]
total_revenue: Metrics
total_products: Metrics
total_users: Metrics
lifetime_sales: Metrics


class UserMetrics(BaseModel):
total_revenue: Dict[str, Union[float, str]]
subscriptions: Dict[str, Union[int, str]]
sales: Dict[str, Union[int, str]]
active_now: Dict[str, Union[int, str]]
revenue: Metrics
subscriptions: Metrics
orders: Metrics
active_users: ActiveUsersMetrics


class AnalyticsSummaryResponse(BaseModel):
Expand Down
48 changes: 34 additions & 14 deletions api/v1/services/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,16 +143,37 @@ def get_analytics_summary(self, token: Annotated[OAuth2, Depends(oauth2_scheme)]

if user.is_super_admin:
data = self.get_summary_data_super_admin(db, start_date, end_date)
message = "Dashboard statistics retrieved successfully"
message = "Admin Statistics Fetched"
else:
user_organization = (db.query(user_organization_association)
.filter_by(user_id=user.id).first())
user_organization = db.query(
user_organization_association).filter_by(user_id=user.id).first()
if not user_organization:
raise HTTPException(
status_code=403, detail="User is not part of any organization")
data = self.get_summary_data_organization(
db, user_organization.organization_id, start_date, end_date)
message = "Dashboard statistics retrieved successfully"
data = {
"revenue": {
"current_month": 0,
"previous_month": 0,
"percentage_difference": "0.00%"
},
"subscriptions": {
"current_month": 0,
"previous_month": 0,
"percentage_difference": "0.00%"
},
"orders": {
"current_month": 0,
"previous_month": 0,
"percentage_difference": "0.00%"
},
"active_users": {
"current": 0,
"difference_an_hour_ago": 0
}
}
message = "User is not part of any organization"
else:
data = self.get_summary_data_organization(
db, user_organization.organization_id, start_date, end_date)
message = "User Statistics Fetched"

return AnalyticsSummaryResponse(
message=message,
Expand Down Expand Up @@ -232,7 +253,7 @@ def get_summary_data_organization(self, db: Session, org_id: str, start_date: da
)).scalar() or 0

return {
"total_revenue": {
"revenue": {
"current_month": total_revenue,
"previous_month": last_month_revenue,
"percentage_difference": f"{self.calculate_percentage_increase(last_month_revenue, total_revenue)}%"
Expand All @@ -242,15 +263,14 @@ def get_summary_data_organization(self, db: Session, org_id: str, start_date: da
"previous_month": last_month_subscriptions,
"percentage_difference": f"{self.calculate_percentage_increase(last_month_subscriptions, subscriptions)}%"
},
"sales": {
"orders": {
"current_month": sales,
"previous_month": last_month_sales,
"percentage_difference": f"{self.calculate_percentage_increase(last_month_sales, sales)}%"
},
"active_now": {
"current_hour": active_now,
"previous_hour": active_previous_hour,
"percentage_difference": f"{self.calculate_percentage_increase(active_previous_hour, active_now)}%"
"active_users": {
"current": active_now,
"difference_an_hour_ago": active_now - active_previous_hour
}
}

Expand Down
45 changes: 21 additions & 24 deletions tests/v1/analytics/test_analytics_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from fastapi.testclient import TestClient
from unittest.mock import MagicMock, patch
from datetime import datetime, timedelta
from api.v1.routes.statistics import get_analytics_summary
from api.v1.routes.dashboard import get_analytics_summary
from api.v1.services.analytics import AnalyticsServices
from api.v1.schemas.analytics import AnalyticsSummaryResponse
from main import app
Expand Down Expand Up @@ -48,7 +48,7 @@ def mock_db_session():

def test_statistics_summary_super_admin(mock_analytics_service, mock_oauth2_scheme, mock_get_current_user_super_admin, mock_db_session):
expected_response = AnalyticsSummaryResponse(
message="Dashboard statistics retrieved successfully",
message="Admin Statistics Fetched",
status='success',
status_code=200,
data={
Expand Down Expand Up @@ -80,22 +80,22 @@ def test_statistics_summary_super_admin(mock_analytics_service, mock_oauth2_sche
end_date = datetime.utcnow()

response = client.get(
"/api/v1/statistics",
"/api/v1/dashboard/statistics",
headers={"Authorization": f"Bearer {token}"},
params={"start_date": "2024-07-09T00:00:00", "end_date": "2024-08-08T00:00:00"}
params={"start_date": "2024-07-09T00:00:00",
"end_date": "2024-08-08T00:00:00"}
)

assert response.status_code == 200



def test_statistics_summary_user(mock_analytics_service, mock_oauth2_scheme, mock_get_current_user_user, mock_db_session):
expected_response = AnalyticsSummaryResponse(
message="Dashboard statistics retrieved successfully",
message="User Statistics Fetched",
status='success',
status_code=200,
data={
"total_revenue": {
"revenue": {
"current_month": 5000,
"previous_month": 4500,
"percentage_difference": "11.11%"
Expand All @@ -105,15 +105,14 @@ def test_statistics_summary_user(mock_analytics_service, mock_oauth2_scheme, moc
"previous_month": 90,
"percentage_difference": "11.11%"
},
"sales": {
"orders": {
"current_month": 150,
"previous_month": 135,
"percentage_difference": "11.11%"
},
"active_now": {
"current_hour": 25,
"previous_hour": 22,
"percentage_difference": "13.64%"
"active_users": {
"current": 25,
"difference_an_hour_ago": 13
}
}
)
Expand All @@ -123,22 +122,22 @@ def test_statistics_summary_user(mock_analytics_service, mock_oauth2_scheme, moc
end_date = datetime.utcnow()

response = client.get(
"/api/v1/statistics",
"/api/v1/dashboard/statistics",
headers={"Authorization": f"Bearer {token}"},
params={"start_date": "2024-07-09T00:00:00", "end_date": "2024-08-08T00:00:00"}
params={"start_date": "2024-07-09T00:00:00",
"end_date": "2024-08-08T00:00:00"}
)

assert response.status_code == 200



def test_statistics_summary_no_dates(mock_analytics_service, mock_oauth2_scheme, mock_get_current_user_user, mock_db_session):
expected_response = AnalyticsSummaryResponse(
message="Dashboard statistics retrieved successfully",
message="User Statistics Fetched",
status='success',
status_code=200,
data={
"total_revenue": {
"revenue": {
"current_month": 3000,
"previous_month": 2700,
"percentage_difference": "11.11%"
Expand All @@ -148,25 +147,23 @@ def test_statistics_summary_no_dates(mock_analytics_service, mock_oauth2_scheme,
"previous_month": 67,
"percentage_difference": "11.94%"
},
"sales": {
"orders": {
"current_month": 120,
"previous_month": 108,
"percentage_difference": "11.11%"
},
"active_now": {
"current_hour": 20,
"previous_hour": 18,
"percentage_difference": "11.11%"
"active_users": {
"current": 25,
"difference_an_hour_ago": 11
}
}
)

token = "user_token"

response = client.get(
"/api/v1/statistics",
"/api/v1/dashboard/statistics",
headers={"Authorization": f"Bearer {token}"}
)

assert response.status_code == 200

0 comments on commit 506e2cb

Please sign in to comment.