"""Organization structure models — departments and members synced from Feishu.""" import uuid from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, func from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base class OrgDepartment(Base): """Department from Feishu org structure.""" __tablename__ = "org_departments" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) external_id: Mapped[str | None] = mapped_column(String(100), index=True) provider_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), nullable=True) # No FK - soft coupling name: Mapped[str] = mapped_column(String(200), nullable=False) parent_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("org_departments.id")) path: Mapped[str] = mapped_column(String(500), default="") member_count: Mapped[int] = mapped_column(Integer, default=0) status: Mapped[str] = mapped_column(String(20), default="active") tenant_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("tenants.id"), index=True) synced_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) members: Mapped[list["OrgMember"]] = relationship(back_populates="department") # provider: Mapped["IdentityProvider | None"] = relationship() # Removed - use program to query class OrgMember(Base): """Person from an identity provider's org structure.""" __tablename__ = "org_members" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) # Generic identity fields (use these instead of provider-specific fields) open_id: Mapped[str | None] = mapped_column(String(100), index=True) unionid: Mapped[str | None] = mapped_column(String(100), index=True) external_id: Mapped[str | None] = mapped_column(String(100), index=True) provider_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), nullable=True) # No FK - soft coupling name: Mapped[str] = mapped_column(String(100), nullable=False) name_translit_full: Mapped[str | None] = mapped_column(String(255), index=True) name_translit_initial: Mapped[str | None] = mapped_column(String(50), index=True) email: Mapped[str | None] = mapped_column(String(200)) avatar_url: Mapped[str | None] = mapped_column(String(500)) title: Mapped[str] = mapped_column(String(200), default="") department_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("org_departments.id")) department_path: Mapped[str] = mapped_column(String(500), default="") phone: Mapped[str | None] = mapped_column(String(50)) status: Mapped[str] = mapped_column(String(20), default="active") tenant_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("tenants.id"), index=True) user_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), nullable=True) # No FK - soft coupling synced_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) department: Mapped["OrgDepartment | None"] = relationship(back_populates="members") class AgentRelationship(Base): """Relationship between an agent and an org member.""" __tablename__ = "agent_relationships" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) agent_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("agents.id", ondelete="CASCADE"), nullable=False) member_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("org_members.id"), nullable=False) relation: Mapped[str] = mapped_column(String(50), nullable=False, default="collaborator") description: Mapped[str] = mapped_column(Text, default="") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) member: Mapped["OrgMember"] = relationship() class AgentAgentRelationship(Base): """Relationship between two agents (digital employees).""" __tablename__ = "agent_agent_relationships" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) agent_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("agents.id", ondelete="CASCADE"), nullable=False) target_agent_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("agents.id", ondelete="CASCADE"), nullable=False) relation: Mapped[str] = mapped_column(String(50), nullable=False, default="collaborator") description: Mapped[str] = mapped_column(Text, default="") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) target_agent = relationship("Agent", foreign_keys=[target_agent_id])