# /opt/docker/dev/service_finder/backend/app/models/gamification/gamification.py import uuid from datetime import datetime, date from typing import Optional, List, TYPE_CHECKING from sqlalchemy import ForeignKey, String, Integer, DateTime, func, Boolean, Text, text, Date from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB from app.database import Base # MB 2.0: Központi Base if TYPE_CHECKING: from app.models.identity import User class PointRule(Base): __tablename__ = "point_rules" __table_args__ = {"schema": "gamification", "extend_existing": True} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) action_key: Mapped[str] = mapped_column(String, unique=True, index=True) points: Mapped[int] = mapped_column(Integer, default=0) description: Mapped[Optional[str]] = mapped_column(String) is_active: Mapped[bool] = mapped_column(Boolean, default=True) class LevelConfig(Base): __tablename__ = "level_configs" __table_args__ = {"schema": "gamification", "extend_existing": True} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) level_number: Mapped[int] = mapped_column(Integer, unique=True) min_points: Mapped[int] = mapped_column(Integer) rank_name: Mapped[str] = mapped_column(String) class PointsLedger(Base): __tablename__ = "points_ledger" __table_args__ = {"schema": "gamification", "extend_existing": True} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) # MB 2.0: User az identity sémában lakik! user_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id")) points: Mapped[int] = mapped_column(Integer, default=0) penalty_change: Mapped[int] = mapped_column(Integer, server_default=text("0"), default=0) reason: Mapped[str] = mapped_column(String) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) user: Mapped["User"] = relationship("User") class UserStats(Base): __tablename__ = "user_stats" __table_args__ = {"schema": "gamification", "extend_existing": True} # MB 2.0: User az identity sémában lakik! user_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), primary_key=True) total_xp: Mapped[int] = mapped_column(Integer, default=0) social_points: Mapped[int] = mapped_column(Integer, default=0) current_level: Mapped[int] = mapped_column(Integer, default=1) penalty_points: Mapped[int] = mapped_column(Integer, server_default=text("0"), default=0) restriction_level: Mapped[int] = mapped_column(Integer, server_default=text("0"), default=0) penalty_quota_remaining: Mapped[int] = mapped_column(Integer, nullable=False, default=0) places_discovered: Mapped[int] = mapped_column(Integer, default=0, server_default=text("0")) places_validated: Mapped[int] = mapped_column(Integer, default=0, server_default=text("0")) banned_until: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) user: Mapped["User"] = relationship("User", back_populates="stats") class Badge(Base): __tablename__ = "badges" __table_args__ = {"schema": "gamification", "extend_existing": True} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) name: Mapped[str] = mapped_column(String, unique=True) description: Mapped[str] = mapped_column(String) icon_url: Mapped[Optional[str]] = mapped_column(String) class UserBadge(Base): __tablename__ = "user_badges" __table_args__ = {"schema": "gamification", "extend_existing": True} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) # MB 2.0: User az identity sémában lakik! user_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id")) badge_id: Mapped[int] = mapped_column(Integer, ForeignKey("gamification.badges.id")) earned_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) user: Mapped["User"] = relationship("User") class UserContribution(Base): """ Felhasználói hozzájárulások nyilvántartása (szerviz beküldés, validálás, jelentés). Ez a tábla tárolja, hogy melyik felhasználó milyen tevékenységet végzett és milyen jutalmat kapott. """ __tablename__ = "user_contributions" __table_args__ = {"schema": "gamification"} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) user_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=False, index=True) season_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("gamification.seasons.id"), nullable=True, index=True) # --- HIÁNYZÓ MEZŐK PÓTOLVA A SPAM VÉDELEMHEZ --- service_fingerprint: Mapped[Optional[str]] = mapped_column(String(255), index=True) cooldown_end: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) action_type: Mapped[int] = mapped_column(Integer, nullable=False) earned_xp: Mapped[int] = mapped_column(Integer, nullable=False) contribution_type: Mapped[str] = mapped_column(String(50), nullable=False, index=True) # 'service_submission', 'service_validation', 'report_abuse' entity_type: Mapped[Optional[str]] = mapped_column(String(50), index=True) # 'service', 'review', 'comment' entity_id: Mapped[Optional[int]] = mapped_column(Integer, index=True) # ID of the contributed entity points_awarded: Mapped[int] = mapped_column(Integer, default=0) xp_awarded: Mapped[int] = mapped_column(Integer, default=0) status: Mapped[str] = mapped_column(String(20), default="pending", index=True) # 'pending', 'approved', 'rejected' reviewed_by: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=True) reviewed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) # --- JAVÍTOTT FOGLALT SZÓ --- provided_fields: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) # Relationships user: Mapped["User"] = relationship("User", foreign_keys=[user_id]) reviewer: Mapped[Optional["User"]] = relationship("User", foreign_keys=[reviewed_by]) season: Mapped[Optional["Season"]] = relationship("Season") class Season(Base): """ Szezonális versenyek tárolása. """ __tablename__ = "seasons" __table_args__ = {"schema": "gamification"} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) name: Mapped[str] = mapped_column(String(100), nullable=False) start_date: Mapped[date] = mapped_column(Date, nullable=False) end_date: Mapped[date] = mapped_column(Date, nullable=False) is_active: Mapped[bool] = mapped_column(Boolean, default=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())