Backend: - New models: MatchFeedback, EfficiencyProfile, EfficiencyRate, ToolEfficiency, ToolEfficiencyRate - 3 preset profiles seeded: Conservative, Moderate, Aggressive with per-discipline rates - 6 BTG tools seeded: Pencil, OMG, Creative X, Cortex, Semblance, Share of Model - Efficiency API: CRUD for profiles and tools at /api/efficiency/ - team_shape.py: accepts profile_rates + tool_rates (per-discipline, additive, capped at 90%) - team-shape endpoint: accepts profile_id and tool_ids query params - Programme roles always exempt regardless of method Example: Moderate profile + Creative X + Pencil → Account Mgmt 10%, Creative 70%, Production 65% Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
71 lines
3.1 KiB
Python
71 lines
3.1 KiB
Python
"""Models for match feedback, efficiency profiles, and tool efficiencies."""
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import String, Text, Integer, Numeric, Boolean, DateTime, ForeignKey, JSON
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.database import Base
|
|
|
|
|
|
class MatchFeedback(Base):
|
|
"""Stores confirmed/rejected match mappings for learning."""
|
|
__tablename__ = "match_feedback"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
client_term: Mapped[str] = mapped_column(String(500), index=True)
|
|
client_description: Mapped[str | None] = mapped_column(Text)
|
|
gmal_asset_id: Mapped[int] = mapped_column(ForeignKey("gmal_assets.id"), nullable=False)
|
|
confirmed: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
user_comment: Mapped[str | None] = mapped_column(Text)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
|
|
gmal_asset: Mapped["GmalAsset"] = relationship()
|
|
|
|
|
|
class EfficiencyProfile(Base):
|
|
"""Named efficiency profile (Conservative, Moderate, Aggressive, Custom)."""
|
|
__tablename__ = "efficiency_profiles"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
name: Mapped[str] = mapped_column(String(100), nullable=False, unique=True)
|
|
is_default: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
|
|
rates: Mapped[list["EfficiencyRate"]] = relationship(back_populates="profile", cascade="all, delete-orphan")
|
|
|
|
|
|
class EfficiencyRate(Base):
|
|
"""Per-discipline efficiency rate within a profile."""
|
|
__tablename__ = "efficiency_rates"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
profile_id: Mapped[int] = mapped_column(ForeignKey("efficiency_profiles.id", ondelete="CASCADE"), nullable=False)
|
|
discipline: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
efficiency_pct: Mapped[float] = mapped_column(Numeric(5, 2), nullable=False)
|
|
|
|
profile: Mapped["EfficiencyProfile"] = relationship(back_populates="rates")
|
|
|
|
|
|
class ToolEfficiency(Base):
|
|
"""A BTG tool that provides additional efficiency."""
|
|
__tablename__ = "tool_efficiencies"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
tool_name: Mapped[str] = mapped_column(String(100), nullable=False, unique=True)
|
|
tool_description: Mapped[str | None] = mapped_column(Text)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
|
|
rates: Mapped[list["ToolEfficiencyRate"]] = relationship(back_populates="tool", cascade="all, delete-orphan")
|
|
|
|
|
|
class ToolEfficiencyRate(Base):
|
|
"""Per-discipline efficiency delta for a specific tool."""
|
|
__tablename__ = "tool_efficiency_rates"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
tool_id: Mapped[int] = mapped_column(ForeignKey("tool_efficiencies.id", ondelete="CASCADE"), nullable=False)
|
|
discipline: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
additional_efficiency_pct: Mapped[float] = mapped_column(Numeric(5, 2), nullable=False)
|
|
|
|
tool: Mapped["ToolEfficiency"] = relationship(back_populates="rates")
|