MAC / mac /models /notification.py
Aaryan17's picture
chore: upload MAC codebase to HF Space
0e76632 verified
"""Notification and audit log models."""
import uuid
from datetime import datetime, timezone
from sqlalchemy import String, Boolean, Integer, DateTime, Text, ForeignKey, JSON
from sqlalchemy.orm import Mapped, mapped_column
from mac.database import Base
def _utcnow():
return datetime.now(timezone.utc)
def _gen_uuid():
return str(uuid.uuid4())
class Notification(Base):
"""Push/in-app notification for a user."""
__tablename__ = "notifications"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_gen_uuid)
user_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
title: Mapped[str] = mapped_column(String(200), nullable=False)
body: Mapped[str] = mapped_column(Text, nullable=False, default="")
category: Mapped[str] = mapped_column(String(50), nullable=False, default="general")
# general | doubt_reply | attendance | system | admin
link: Mapped[str | None] = mapped_column(String(500), nullable=True)
is_read: Mapped[bool] = mapped_column(Boolean, default=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
class PushSubscription(Base):
"""Web Push subscription for browser notifications."""
__tablename__ = "push_subscriptions"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_gen_uuid)
user_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
endpoint: Mapped[str] = mapped_column(Text, nullable=False)
p256dh_key: Mapped[str] = mapped_column(String(200), nullable=False)
auth_key: Mapped[str] = mapped_column(String(200), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
class AuditLog(Base):
"""Comprehensive audit trail for admin/faculty/system actions."""
__tablename__ = "audit_logs"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_gen_uuid)
actor_id: Mapped[str | None] = mapped_column(String(36), nullable=True) # user_id or "system"
actor_role: Mapped[str] = mapped_column(String(20), nullable=False, default="system")
action: Mapped[str] = mapped_column(String(100), nullable=False)
# e.g. user.login, key.create, node.enroll, model.deploy, attendance.mark, doubt.reply
resource_type: Mapped[str] = mapped_column(String(50), nullable=False, default="system")
resource_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
details: Mapped[str] = mapped_column(Text, nullable=True) # JSON-encoded before/after or extra info
ip_address: Mapped[str | None] = mapped_column(String(45), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow, index=True)
class ScopedApiKey(Base):
"""Advanced API key with scoped permissions, rate limits, and expiry."""
__tablename__ = "scoped_api_keys"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_gen_uuid)
user_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
name: Mapped[str] = mapped_column(String(100), nullable=False)
key_prefix: Mapped[str] = mapped_column(String(20), nullable=False) # first 8 chars for display
key_hash: Mapped[str] = mapped_column(String(200), nullable=False, unique=True)
# Scoping
allowed_models: Mapped[str] = mapped_column(Text, nullable=True) # JSON list of model IDs, null = all
allowed_endpoints: Mapped[str] = mapped_column(Text, nullable=True) # JSON list, null = all
# Limits
requests_per_hour: Mapped[int] = mapped_column(Integer, nullable=False, default=100)
tokens_per_day: Mapped[int] = mapped_column(Integer, nullable=False, default=50000)
max_tokens_per_request: Mapped[int] = mapped_column(Integer, nullable=False, default=4096)
# State
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
last_used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
total_requests: Mapped[int] = mapped_column(Integer, default=0)
total_tokens: Mapped[int] = mapped_column(Integer, default=0)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
revoked_by: Mapped[str | None] = mapped_column(String(36), nullable=True)