from __future__ import annotations import uuid from datetime import datetime from typing import Any, Self from pydantic import BaseModel, ConfigDict, Field, field_validator from app.models.company import Company from app.models.enums import CompanyStatus, EnrichmentStatus, MonitoringFrequency, SeverityLevel def _normalize_website(value: str | None) -> str | None: if value is None or value.strip() == "": return None value = value.strip() if not (value.startswith("http://") or value.startswith("https://")): value = f"https://{value}" return value class CompanyCreate(BaseModel): name: str = Field(min_length=1, max_length=200) official_website: str | None = Field(default=None, max_length=500) description: str | None = Field(default=None, max_length=4000) monitoring_focus: str | None = Field(default=None, max_length=2000) industry: str | None = Field(default=None, max_length=120) country: str | None = Field(default=None, max_length=120) region: str | None = Field(default=None, max_length=120) headquarters: str | None = Field(default=None, max_length=200) public_identifiers: dict[str, str] = Field(default_factory=dict) competitor_names: list[str] = Field(default_factory=list, max_length=25) alias_names: list[str] = Field(default_factory=list, max_length=25) frequency_type: MonitoringFrequency = MonitoringFrequency.WEEKLY interval_minutes: int | None = Field(default=None, ge=1) cron_expression: str | None = Field(default=None, max_length=120) timezone: str = Field(default="America/New_York", max_length=64) severity_threshold: SeverityLevel = SeverityLevel.MEDIUM @field_validator("official_website") @classmethod def _validate_website(cls, value: str | None) -> str | None: return _normalize_website(value) class CompanyUpdate(BaseModel): name: str | None = Field(default=None, min_length=1, max_length=200) official_website: str | None = Field(default=None, max_length=500) description: str | None = Field(default=None, max_length=4000) monitoring_focus: str | None = Field(default=None, max_length=2000) industry: str | None = Field(default=None, max_length=120) country: str | None = Field(default=None, max_length=120) region: str | None = Field(default=None, max_length=120) headquarters: str | None = Field(default=None, max_length=200) public_identifiers: dict[str, str] | None = None competitor_names: list[str] | None = Field(default=None, max_length=25) alias_names: list[str] | None = Field(default=None, max_length=25) @field_validator("official_website") @classmethod def _validate_website(cls, value: str | None) -> str | None: return _normalize_website(value) class MonitorConfigurationResponse(BaseModel): model_config = ConfigDict(from_attributes=True) id: uuid.UUID frequency_type: MonitoringFrequency interval_minutes: int | None cron_expression: str | None timezone: str enabled: bool next_run: datetime | None last_run: datetime | None severity_threshold: SeverityLevel class MonitorConfigurationUpdate(BaseModel): frequency_type: MonitoringFrequency | None = None interval_minutes: int | None = Field(default=None, ge=1) cron_expression: str | None = Field(default=None, max_length=120) timezone: str | None = Field(default=None, max_length=64) enabled: bool | None = None severity_threshold: SeverityLevel | None = None class CompanyEnrichmentResponse(BaseModel): model_config = ConfigDict(from_attributes=True) status: EnrichmentStatus data: dict[str, Any] = Field(default_factory=dict) errors: dict[str, str] = Field(default_factory=dict) credits_spent: int | None fetched_at: datetime | None class CompanyResponse(BaseModel): model_config = ConfigDict(from_attributes=True) id: uuid.UUID name: str slug: str official_website: str | None description: str | None monitoring_focus: str | None industry: str | None country: str | None region: str | None headquarters: str | None public_identifiers: dict[str, str] = Field(default_factory=dict) status: CompanyStatus created_at: datetime updated_at: datetime aliases: list[str] = Field(default_factory=list) competitors: list[str] = Field(default_factory=list) monitor_configuration: MonitorConfigurationResponse | None = None # None until the onboarding-time enrichment task finishes (or if # NINJAPEAR_API_KEY was never configured) - see enrichment_service.py. enrichment: CompanyEnrichmentResponse | None = None # Report/alert counts wire up once those models exist (Phases 7-8). report_count: int = 0 unresolved_alert_count: int = 0 @classmethod def from_company( cls, company: Company, *, report_count: int = 0, unresolved_alert_count: int = 0, ) -> Self: return cls( id=company.id, name=company.name, slug=company.slug, official_website=company.official_website, description=company.description, monitoring_focus=company.monitoring_focus, industry=company.industry, country=company.country, region=company.region, headquarters=company.headquarters, public_identifiers=company.public_identifiers, status=company.status, created_at=company.created_at, updated_at=company.updated_at, aliases=[a.alias for a in company.aliases], competitors=[c.name for c in company.competitors], monitor_configuration=( MonitorConfigurationResponse.model_validate(company.monitor_configuration) if company.monitor_configuration else None ), enrichment=( CompanyEnrichmentResponse.model_validate(company.enrichment) if company.enrichment else None ), report_count=report_count, unresolved_alert_count=unresolved_alert_count, )