174 lines
8.2 KiB
Python
174 lines
8.2 KiB
Python
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))
|