bump project

This commit is contained in:
2026-05-01 21:24:21 +03:30
parent dc4b1a1400
commit 139b3e26da
83 changed files with 2455 additions and 18349 deletions

68
app/models/account.py Normal file
View File

@@ -0,0 +1,68 @@
from __future__ import annotations
from datetime import datetime, timezone
from enum import Enum
from flask_login import UserMixin
from sqlalchemy import Boolean, DateTime, String
from sqlalchemy.orm import Mapped, mapped_column, relationship
from werkzeug.security import check_password_hash, generate_password_hash
from app.extensions import db
def utcnow() -> datetime:
return datetime.now(timezone.utc)
class Role(str, Enum):
ADMIN = "admin"
OPERATOR = "operator"
VIEWER = "viewer"
@property
def label(self) -> str:
return self.value.title()
class Account(UserMixin, db.Model):
__tablename__ = "accounts"
id: Mapped[int] = mapped_column(primary_key=True)
username: Mapped[str] = mapped_column(String(80), unique=True, index=True, nullable=False)
email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False)
full_name: Mapped[str] = mapped_column(String(160), nullable=False)
role: Mapped[str] = mapped_column(String(20), default=Role.VIEWER.value, nullable=False)
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, nullable=False)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=utcnow,
onupdate=utcnow,
nullable=False,
)
attendance_events = relationship("AttendanceEvent", back_populates="created_by")
@property
def is_active(self) -> bool:
return self.active
def set_password(self, password: str) -> None:
self.password_hash = generate_password_hash(password, method="scrypt")
def check_password(self, password: str) -> bool:
return check_password_hash(self.password_hash, password)
def has_role(self, *roles: str | Role) -> bool:
normalized = {role.value if isinstance(role, Role) else role for role in roles}
return self.role in normalized
@property
def is_admin(self) -> bool:
return self.role == Role.ADMIN.value
@property
def can_operate(self) -> bool:
return self.role in {Role.ADMIN.value, Role.OPERATOR.value}