morozovka-portal/app/models.py

174 lines
8.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from sqlmodel import SQLModel, Field
from typing import Optional
from datetime import datetime, timezone
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
gitea_id: Optional[int] = Field(default=None, unique=True, index=True)
username: str = Field(unique=True, index=True)
password_hash: Optional[str] = None
full_name: Optional[str] = None
email: Optional[str] = Field(default=None, unique=True)
organization: Optional[str] = None
role: str = Field(default="student")
is_active: bool = Field(default=True)
is_verified: bool = Field(default=False)
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class UserSession(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
session_token: str = Field(unique=True, index=True)
user_id: int = Field(foreign_key="user.id")
ip_address: Optional[str] = None
user_agent: Optional[str] = None
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class Event(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
title: str
event_type: str = Field(default="lecture")
start_time: datetime
end_time: datetime
location: Optional[str] = None
description: Optional[str] = None
status: str = Field(default="scheduled")
is_public: bool = Field(default=True)
google_event_id: Optional[str] = Field(default=None, unique=True, index=True)
manually_edited: bool = Field(default=False)
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class GlobalStatus(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
status: str = Field(default="free")
delay_minutes: int = Field(default=0)
message: Optional[str] = None
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class Project(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
title: str
description: Optional[str] = None
status: str = Field(default="idea")
visibility: str = Field(default="public")
gitea_url: Optional[str] = None
gitea_repo: Optional[str] = None
owner_id: int = Field(foreign_key="user.id")
tags: Optional[str] = None
start_date: Optional[str] = None
end_date: Optional[str] = None
contact_person: Optional[str] = None
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class ProjectMember(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
project_id: int = Field(foreign_key="project.id")
user_id: int = Field(foreign_key="user.id")
role: str = Field(default="viewer")
joined_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class ProjectTask(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
project_id: int = Field(foreign_key="project.id")
title: str
description: Optional[str] = None
status: str = Field(default="backlog")
priority: str = Field(default="medium")
assignee_id: Optional[int] = Field(default=None, foreign_key="user.id")
created_by: int = Field(foreign_key="user.id")
due_date: Optional[str] = None
position: int = Field(default=0)
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class PortalFile(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
original_name: str
stored_name: str
size_bytes: int
uploaded_by: int = Field(foreign_key="user.id")
project_id: Optional[int] = Field(default=None, foreign_key="project.id")
uploaded_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class EventSubscriber(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
email: str = Field(index=True)
user_id: Optional[int] = Field(default=None, foreign_key="user.id")
unsubscribe_token: str = Field(unique=True, index=True)
is_confirmed: bool = Field(default=False)
confirm_token: Optional[str] = Field(default=None, unique=True, index=True)
notify_status: bool = Field(default=True)
notify_events: bool = Field(default=True)
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
# ══════════════════════════════════════════════════════════════════════
# МАСТЕР-КЛАССЫ
# ══════════════════════════════════════════════════════════════════════
class Workshop(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
title: str
description: Optional[str] = None
program: Optional[str] = None
lead_id: int = Field(foreign_key="user.id")
category: str = Field(default="other")
difficulty: str = Field(default="beginner") # beginner, intermediate, advanced
partner: Optional[str] = None # ННГУ, Неймарк, Ядро, ...
status: str = Field(default="draft") # draft, published, archived
visibility: str = Field(default="public") # public, internal
location: Optional[str] = None
materials_required: Optional[str] = None
outcome: Optional[str] = None # что получит участник
has_certificate: bool = Field(default=False)
max_participants: Optional[int] = None
cover_url: Optional[str] = None
tags: Optional[str] = None
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class WorkshopSession(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
workshop_id: int = Field(foreign_key="workshop.id")
start_time: datetime
end_time: datetime
location_override: Optional[str] = None
note: Optional[str] = None
class WorkshopRegistration(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
workshop_id: int = Field(foreign_key="workshop.id")
user_id: int = Field(foreign_key="user.id")
status: str = Field(default="registered")
notes: Optional[str] = None
registered_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class WorkshopMaterial(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
workshop_id: int = Field(foreign_key="workshop.id")
title: str
description: Optional[str] = None
file_id: Optional[int] = Field(default=None, foreign_key="portalfile.id")
external_url: Optional[str] = None
visible_to: str = Field(default="registered")
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class WorkshopFeedback(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
workshop_id: int = Field(foreign_key="workshop.id")
user_id: int = Field(foreign_key="user.id")
rating: int = Field(default=5)
comment: Optional[str] = None
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
# ══════════════════════════════════════════════════════════════════════
# УВЕДОМЛЕНИЯ
# ══════════════════════════════════════════════════════════════════════
class Notification(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
user_id: int = Field(foreign_key="user.id", index=True)
title: str
body: Optional[str] = None
link_url: Optional[str] = None
kind: str = Field(default="info") # info, success, warning, error
is_read: bool = Field(default=False, index=True)
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))