Add superadmin routes to API
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
"""Analytics service for platform-wide statistics."""
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from gatehouse_app.models.organization.organization import Organization
|
||||
from gatehouse_app.models.user.user import User
|
||||
from gatehouse_app.models.user.session import Session
|
||||
from gatehouse_app.models.superadmin_audit_log import SuperadminAuditLog
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SuperadminAnalyticsService:
|
||||
"""Service for platform-wide analytics and statistics."""
|
||||
|
||||
@staticmethod
|
||||
def get_dashboard_stats() -> dict:
|
||||
"""Get dashboard statistics for the overview page.
|
||||
|
||||
Returns:
|
||||
Dashboard stats including org count, user count, etc.
|
||||
"""
|
||||
now = datetime.now(timezone.utc)
|
||||
thirty_days_ago = now - timedelta(days=30)
|
||||
|
||||
# Total organizations
|
||||
total_orgs = Organization.query.filter(Organization.deleted_at.is_(None)).count()
|
||||
active_orgs = Organization.query.filter(
|
||||
Organization.deleted_at.is_(None),
|
||||
Organization.is_active == True, # noqa: E712
|
||||
).count()
|
||||
|
||||
# Total users
|
||||
total_users = User.query.filter(User.deleted_at.is_(None)).count()
|
||||
|
||||
# Active sessions
|
||||
active_sessions = Session.query.filter(
|
||||
Session.deleted_at.is_(None),
|
||||
Session.status == "active",
|
||||
).count()
|
||||
|
||||
# New signups in last 30 days
|
||||
new_users_30d = User.query.filter(
|
||||
User.deleted_at.is_(None),
|
||||
User.created_at >= thirty_days_ago,
|
||||
).count()
|
||||
|
||||
# New organizations in last 30 days
|
||||
new_orgs_30d = Organization.query.filter(
|
||||
Organization.deleted_at.is_(None),
|
||||
Organization.created_at >= thirty_days_ago,
|
||||
).count()
|
||||
|
||||
# Suspended organizations
|
||||
suspended_orgs = Organization.query.filter(
|
||||
Organization.deleted_at.is_(None),
|
||||
Organization.is_active == False, # noqa: E712
|
||||
).count()
|
||||
|
||||
return {
|
||||
"total_organizations": total_orgs,
|
||||
"active_organizations": active_orgs,
|
||||
"suspended_organizations": suspended_orgs,
|
||||
"total_users": total_users,
|
||||
"active_sessions": active_sessions,
|
||||
"new_users_30d": new_users_30d,
|
||||
"new_orgs_30d": new_orgs_30d,
|
||||
"generated_at": now.isoformat() + "Z",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_signup_trends(days: int = 30) -> dict:
|
||||
"""Get signup trends over time.
|
||||
|
||||
Args:
|
||||
days: Number of days to analyze
|
||||
|
||||
Returns:
|
||||
Daily signup data
|
||||
"""
|
||||
now = datetime.now(timezone.utc)
|
||||
start_date = now - timedelta(days=days)
|
||||
|
||||
# Get all users created in period
|
||||
users = User.query.filter(
|
||||
User.deleted_at.is_(None),
|
||||
User.created_at >= start_date,
|
||||
).all()
|
||||
|
||||
# Group by day
|
||||
daily_signups = {}
|
||||
for i in range(days):
|
||||
date = (start_date + timedelta(days=i)).strftime("%Y-%m-%d")
|
||||
daily_signups[date] = 0
|
||||
|
||||
for user in users:
|
||||
date = user.created_at.strftime("%Y-%m-%d")
|
||||
if date in daily_signups:
|
||||
daily_signups[date] += 1
|
||||
|
||||
# Convert to list
|
||||
history = [
|
||||
{"date": date, "value": count}
|
||||
for date, count in sorted(daily_signups.items())
|
||||
]
|
||||
|
||||
return {
|
||||
"period_start": start_date.isoformat() + "Z",
|
||||
"period_end": now.isoformat() + "Z",
|
||||
"total": len(users),
|
||||
"history": history,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_org_distribution() -> dict:
|
||||
"""Get distribution of organizations by size.
|
||||
|
||||
Returns:
|
||||
Organization size distribution
|
||||
"""
|
||||
orgs = Organization.query.filter(Organization.deleted_at.is_(None)).all()
|
||||
|
||||
distribution = {
|
||||
"solo": 0, # 1 user
|
||||
"small": 0, # 2-10 users
|
||||
"medium": 0, # 11-50 users
|
||||
"large": 0, # 51-200 users
|
||||
"enterprise": 0, # 200+ users
|
||||
}
|
||||
|
||||
for org in orgs:
|
||||
count = org.get_member_count()
|
||||
if count == 1:
|
||||
distribution["solo"] += 1
|
||||
elif count <= 10:
|
||||
distribution["small"] += 1
|
||||
elif count <= 50:
|
||||
distribution["medium"] += 1
|
||||
elif count <= 200:
|
||||
distribution["large"] += 1
|
||||
else:
|
||||
distribution["enterprise"] += 1
|
||||
|
||||
return {
|
||||
"distribution": distribution,
|
||||
"total_orgs": len(orgs),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_recent_activity(limit: int = 20) -> list:
|
||||
"""Get recent superadmin actions.
|
||||
|
||||
Args:
|
||||
limit: Maximum number of actions to return
|
||||
|
||||
Returns:
|
||||
List of recent audit log entries
|
||||
"""
|
||||
logs = SuperadminAuditLog.query.filter(
|
||||
SuperadminAuditLog.deleted_at.is_(None),
|
||||
).order_by(
|
||||
SuperadminAuditLog.created_at.desc()
|
||||
).limit(limit).all()
|
||||
|
||||
return [
|
||||
{
|
||||
"id": log.id,
|
||||
"superadmin_id": log.superadmin_id,
|
||||
"action": log.action,
|
||||
"resource_type": log.resource_type,
|
||||
"resource_id": log.resource_id,
|
||||
"extra_data": log.extra_data,
|
||||
"ip_address": log.ip_address,
|
||||
"user_agent": log.user_agent,
|
||||
"created_at": log.created_at.isoformat() + "Z" if log.created_at else None,
|
||||
}
|
||||
for log in logs
|
||||
]
|
||||
Reference in New Issue
Block a user