Clawith/backend/app/models/org.py

92 lines
4.8 KiB
Python

"""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])