- Tier mapping on projects: configurable label→complexity mapping
- Presets: A/B/C, 1/2/3, Gold/Silver/Bronze
- Stored as JSON on project.tier_mapping
- ClientAsset.client_tier field for tracking which tier an asset belongs to
- GMAL family endpoint: GET /gmal/assets/{id}/family returns all complexity variants
- Looks up by asset_name (NOT by GMAL number increment)
- Verified: families share asset_name across non-sequential GMAL IDs
- Expand to Tiers: POST /projects/{id}/expand-tiers
- Splits each matched asset into N tier variants (one per tier)
- Finds correct GMAL variant by asset_name + complexity_level query
- Creates new ClientAsset + Match per tier with correct GMAL
- Removes original un-tiered asset after expansion
- Frontend: tier preset buttons + expand button on Match Review tab
- Tier tags shown with label → complexity mapping
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
117 lines
2.5 KiB
Python
117 lines
2.5 KiB
Python
from pydantic import BaseModel
|
|
from datetime import datetime
|
|
|
|
|
|
class ProjectCreate(BaseModel):
|
|
name: str
|
|
client_name: str | None = None
|
|
description: str | None = None
|
|
model_type: str = "current_oplus"
|
|
|
|
|
|
class ProjectUpdate(BaseModel):
|
|
name: str | None = None
|
|
client_name: str | None = None
|
|
description: str | None = None
|
|
model_type: str | None = None
|
|
|
|
|
|
class ProjectOut(BaseModel):
|
|
id: int
|
|
name: str
|
|
client_name: str | None
|
|
description: str | None
|
|
model_type: str
|
|
status: str
|
|
source_filename: str | None
|
|
parse_stage: str | None = None
|
|
has_brief_analysis: bool = False
|
|
has_tier_mapping: bool = False
|
|
ai_input_tokens: int = 0
|
|
ai_output_tokens: int = 0
|
|
ai_cost_usd: float = 0
|
|
ai_call_count: int = 0
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
asset_count: int = 0
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class ClientAssetOut(BaseModel):
|
|
id: int
|
|
project_id: int
|
|
raw_name: str | None
|
|
raw_description: str | None
|
|
client_tier: str | None = None
|
|
volume: int
|
|
sort_order: int | None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class ClientAssetUpdate(BaseModel):
|
|
raw_name: str | None = None
|
|
raw_description: str | None = None
|
|
volume: int | None = None
|
|
|
|
|
|
class MatchOut(BaseModel):
|
|
id: int
|
|
client_asset_id: int
|
|
gmal_asset_id: int
|
|
gmal_id: str | None = None
|
|
gmal_name: str | None = None
|
|
gmal_unique_name: str | None = None
|
|
confidence: str
|
|
confidence_score: float | None
|
|
ai_reasoning: str | None
|
|
caveat_text: str | None
|
|
is_selected: bool
|
|
rank: int
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class MatchSelectRequest(BaseModel):
|
|
is_selected: bool = True
|
|
|
|
|
|
class ManualMatchRequest(BaseModel):
|
|
gmal_asset_id: int
|
|
|
|
|
|
class RatecardLineOut(BaseModel):
|
|
id: int
|
|
client_asset_id: int
|
|
client_asset_name: str | None = None
|
|
gmal_asset_id: int
|
|
gmal_id: str | None = None
|
|
role_id: int
|
|
role_title: str | None = None
|
|
discipline: str | None = None
|
|
base_hours: float | None
|
|
volume: int
|
|
total_hours: float | None
|
|
manual_override: float | None
|
|
notes: str | None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class RatecardLineUpdate(BaseModel):
|
|
manual_override: float | None = None
|
|
notes: str | None = None
|
|
|
|
|
|
class RatecardSummary(BaseModel):
|
|
project_id: int
|
|
project_name: str
|
|
model_type: str
|
|
total_assets: int
|
|
total_hours: float
|
|
lines: list[RatecardLineOut]
|