From b74340d639e17df75023bf06d35498478fc024e5 Mon Sep 17 00:00:00 2001 From: Alexa Amundson <118287761+blackboxprogramming@users.noreply.github.com> Date: Sun, 16 Nov 2025 04:34:27 -0600 Subject: [PATCH] Fix dashboard stats aggregation --- backend/app/models/__init__.py | 4 ++ backend/app/routers/dashboard.py | 8 ++-- backend/tests/test_dashboard.py | 68 ++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 backend/tests/test_dashboard.py diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index 8a3ef44..6a8d770 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -4,6 +4,7 @@ from app.models.email import Email, EmailFolder from app.models.social import Post, Comment, Like, Follow from app.models.video import Video, VideoView, VideoLike from app.models.file import File, Folder +from app.models.device import Device, DeviceMetric, DeviceLog from app.models.blockchain import Block, Transaction, Wallet from app.models.ai_chat import Conversation, Message @@ -20,6 +21,9 @@ __all__ = [ "VideoLike", "File", "Folder", + "Device", + "DeviceMetric", + "DeviceLog", "Block", "Transaction", "Wallet", diff --git a/backend/app/routers/dashboard.py b/backend/app/routers/dashboard.py index aed0165..865ad87 100644 --- a/backend/app/routers/dashboard.py +++ b/backend/app/routers/dashboard.py @@ -10,7 +10,7 @@ Provides a comprehensive overview of all integrated services: from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, func +from sqlalchemy import select, func, cast, Integer from typing import Dict, List, Any from datetime import datetime, timedelta import os @@ -435,7 +435,7 @@ async def get_user_stats(db: AsyncSession, user: User) -> Dict[str, Any]: # Social stats posts_result = await db.execute( - select(func.count(Post.id)).filter(Post.author_id == user.id) + select(func.count(Post.id)).filter(Post.user_id == user.id) ) posts_total = posts_result.scalar() or 0 @@ -449,7 +449,7 @@ async def get_user_stats(db: AsyncSession, user: User) -> Dict[str, Any]: # Videos stats videos_result = await db.execute( - select(func.count(Video.id), func.sum(Video.views)).filter(Video.uploader_id == user.id) + select(func.count(Video.id), func.sum(Video.views_count)).filter(Video.user_id == user.id) ) videos_data = videos_result.first() videos_total = videos_data[0] or 0 @@ -472,7 +472,7 @@ async def get_user_stats(db: AsyncSession, user: User) -> Dict[str, Any]: # Devices stats devices_result = await db.execute( - select(func.count(Device.id), func.sum(func.cast(Device.is_online, func.Integer))) + select(func.count(Device.id), func.sum(cast(Device.is_online, Integer))) .filter(Device.owner_id == user.id) ) devices_data = devices_result.first() diff --git a/backend/tests/test_dashboard.py b/backend/tests/test_dashboard.py new file mode 100644 index 0000000..c9a6a2f --- /dev/null +++ b/backend/tests/test_dashboard.py @@ -0,0 +1,68 @@ +"""Tests for dashboard overview statistics""" +import pytest +from httpx import AsyncClient +from sqlalchemy.ext.asyncio import AsyncSession + +from app.models.social import Post +from app.models.video import Video +from app.models.device import Device + + +@pytest.mark.asyncio +async def test_dashboard_overview_reflects_user_content( + client: AsyncClient, + auth_headers, + db_session: AsyncSession, + test_user, +): + """Ensure the dashboard aggregates posts, videos, and devices correctly.""" + + user_id = test_user["id"] + + posts = [ + Post(user_id=user_id, content="Post A"), + Post(user_id=user_id, content="Post B"), + ] + + videos = [ + Video(user_id=user_id, title="Video 1", video_url="http://example.com/1", views_count=10), + Video(user_id=user_id, title="Video 2", video_url="http://example.com/2", views_count=25), + ] + + devices = [ + Device( + device_id="dev-1", + name="Living Room Pi", + device_type="pi4", + owner_id=user_id, + is_online=True, + ), + Device( + device_id="dev-2", + name="Bedroom Pi", + device_type="pi4", + owner_id=user_id, + is_online=False, + ), + ] + + db_session.add_all(posts + videos + devices) + await db_session.commit() + + response = await client.get("/api/dashboard/overview", headers=auth_headers) + assert response.status_code == 200 + payload = response.json() + + def get_service(name: str): + return next(service for service in payload["services"] if service["name"] == name) + + social_stats = get_service("Social Media")["stats"] + assert social_stats["posts"] == len(posts) + + video_stats = get_service("Video Platform")["stats"] + assert video_stats["videos"] == len(videos) + assert video_stats["views"] == sum(video.views_count for video in videos) + + device_stats = get_service("Devices (IoT/Pi)")["stats"] + assert device_stats["total"] == len(devices) + assert device_stats["online"] == sum(1 for device in devices if device.is_online)