intermittent commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
"""Address module database models."""
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Integer, JSON, String, Text, text
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from govoplan_core.db.base import Base, TimestampMixin
|
||||
|
||||
|
||||
def new_uuid() -> str:
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
class AddressBook(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_address_books"
|
||||
__table_args__ = (
|
||||
Index("ix_addresses_address_books_scope", "tenant_id", "scope_type", "scope_id"),
|
||||
Index(
|
||||
"uq_addresses_address_books_active_name",
|
||||
"tenant_id",
|
||||
"scope_type",
|
||||
"scope_id",
|
||||
"name",
|
||||
unique=True,
|
||||
sqlite_where=text("deleted_at IS NULL"),
|
||||
postgresql_where=text("deleted_at IS NULL"),
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
scope_type: Mapped[str] = mapped_column(String(20), nullable=False, index=True)
|
||||
scope_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
source_kind: Mapped[str] = mapped_column(String(30), default="local", nullable=False, index=True)
|
||||
source_ref: Mapped[str | None] = mapped_column(String(1000))
|
||||
read_only: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
sync_status: Mapped[str | None] = mapped_column(String(30))
|
||||
sync_error: Mapped[str | None] = mapped_column(Text)
|
||||
created_by_account_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
updated_by_account_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
metadata_: Mapped[dict[str, Any] | None] = mapped_column("metadata", JSON, nullable=True)
|
||||
|
||||
contacts: Mapped[list["Contact"]] = relationship(back_populates="address_book", cascade="all, delete-orphan")
|
||||
address_lists: Mapped[list["AddressList"]] = relationship(back_populates="address_book", cascade="all, delete-orphan")
|
||||
sync_sources: Mapped[list["AddressSyncSource"]] = relationship(back_populates="address_book", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class Contact(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_contacts"
|
||||
__table_args__ = (
|
||||
Index("ix_addresses_contacts_book_name", "address_book_id", "display_name"),
|
||||
Index("ix_addresses_contacts_tenant_name", "tenant_id", "display_name"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
address_book_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("addresses_address_books.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
display_name: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
|
||||
given_name: Mapped[str | None] = mapped_column(String(255))
|
||||
family_name: Mapped[str | None] = mapped_column(String(255))
|
||||
organization: Mapped[str | None] = mapped_column(String(255), index=True)
|
||||
role_title: Mapped[str | None] = mapped_column(String(255))
|
||||
note: Mapped[str | None] = mapped_column(Text)
|
||||
tags: Mapped[list[str]] = mapped_column(JSON, default=list, nullable=False)
|
||||
source_kind: Mapped[str] = mapped_column(String(30), default="local", nullable=False, index=True)
|
||||
source_ref: Mapped[str | None] = mapped_column(String(1000))
|
||||
source_payload_kind: Mapped[str | None] = mapped_column(String(40), nullable=True, index=True)
|
||||
source_payload_raw: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
source_revision: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True)
|
||||
provenance: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False)
|
||||
created_by_account_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
updated_by_account_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
metadata_: Mapped[dict[str, Any] | None] = mapped_column("metadata", JSON, nullable=True)
|
||||
|
||||
address_book: Mapped[AddressBook] = relationship(back_populates="contacts")
|
||||
emails: Mapped[list["ContactEmail"]] = relationship(back_populates="contact", cascade="all, delete-orphan", order_by="ContactEmail.order_index")
|
||||
phones: Mapped[list["ContactPhone"]] = relationship(back_populates="contact", cascade="all, delete-orphan", order_by="ContactPhone.order_index")
|
||||
postal_addresses: Mapped[list["ContactPostalAddress"]] = relationship(
|
||||
back_populates="contact",
|
||||
cascade="all, delete-orphan",
|
||||
order_by="ContactPostalAddress.order_index",
|
||||
)
|
||||
address_list_entries: Mapped[list["AddressListEntry"]] = relationship(back_populates="contact", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class ContactEmail(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_contact_emails"
|
||||
__table_args__ = (
|
||||
Index("ix_addresses_contact_emails_lookup", "email"),
|
||||
Index("ix_addresses_contact_emails_contact_primary", "contact_id", "is_primary"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
contact_id: Mapped[str] = mapped_column(ForeignKey("addresses_contacts.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
label: Mapped[str | None] = mapped_column(String(80))
|
||||
email: Mapped[str] = mapped_column(String(320), nullable=False, index=True)
|
||||
is_primary: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
order_index: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
|
||||
contact: Mapped[Contact] = relationship(back_populates="emails")
|
||||
address_list_entries: Mapped[list["AddressListEntry"]] = relationship(back_populates="contact_email")
|
||||
|
||||
|
||||
class ContactPhone(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_contact_phones"
|
||||
__table_args__ = (Index("ix_addresses_contact_phones_contact_primary", "contact_id", "is_primary"),)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
contact_id: Mapped[str] = mapped_column(ForeignKey("addresses_contacts.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
label: Mapped[str | None] = mapped_column(String(80))
|
||||
phone: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
is_primary: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
order_index: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
|
||||
contact: Mapped[Contact] = relationship(back_populates="phones")
|
||||
|
||||
|
||||
class ContactPostalAddress(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_contact_postal_addresses"
|
||||
__table_args__ = (Index("ix_addresses_contact_postal_addresses_contact_primary", "contact_id", "is_primary"),)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
contact_id: Mapped[str] = mapped_column(ForeignKey("addresses_contacts.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
label: Mapped[str | None] = mapped_column(String(80))
|
||||
street: Mapped[str | None] = mapped_column(String(500))
|
||||
postal_code: Mapped[str | None] = mapped_column(String(40))
|
||||
locality: Mapped[str | None] = mapped_column(String(255))
|
||||
region: Mapped[str | None] = mapped_column(String(255))
|
||||
country: Mapped[str | None] = mapped_column(String(255))
|
||||
is_primary: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
order_index: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
|
||||
contact: Mapped[Contact] = relationship(back_populates="postal_addresses")
|
||||
address_list_entries: Mapped[list["AddressListEntry"]] = relationship(back_populates="contact_postal_address")
|
||||
|
||||
|
||||
class AddressList(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_address_lists"
|
||||
__table_args__ = (
|
||||
Index("ix_addresses_address_lists_book_name", "address_book_id", "name"),
|
||||
Index(
|
||||
"uq_addresses_address_lists_active_name",
|
||||
"address_book_id",
|
||||
"name",
|
||||
unique=True,
|
||||
sqlite_where=text("deleted_at IS NULL"),
|
||||
postgresql_where=text("deleted_at IS NULL"),
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
address_book_id: Mapped[str] = mapped_column(ForeignKey("addresses_address_books.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
source_kind: Mapped[str] = mapped_column(String(30), default="local", nullable=False, index=True)
|
||||
source_ref: Mapped[str | None] = mapped_column(String(1000))
|
||||
read_only: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
created_by_account_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
updated_by_account_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
metadata_: Mapped[dict[str, Any] | None] = mapped_column("metadata", JSON, nullable=True)
|
||||
|
||||
address_book: Mapped[AddressBook] = relationship(back_populates="address_lists")
|
||||
entries: Mapped[list["AddressListEntry"]] = relationship(
|
||||
back_populates="address_list",
|
||||
cascade="all, delete-orphan",
|
||||
order_by="AddressListEntry.order_index",
|
||||
)
|
||||
|
||||
|
||||
class AddressListEntry(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_address_list_entries"
|
||||
__table_args__ = (
|
||||
Index("ix_addresses_address_list_entries_list_order", "address_list_id", "order_index"),
|
||||
Index("ix_addresses_address_list_entries_contact", "contact_id"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
address_list_id: Mapped[str] = mapped_column(ForeignKey("addresses_address_lists.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
contact_id: Mapped[str] = mapped_column(ForeignKey("addresses_contacts.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
contact_email_id: Mapped[str | None] = mapped_column(ForeignKey("addresses_contact_emails.id", ondelete="SET NULL"), nullable=True, index=True)
|
||||
contact_postal_address_id: Mapped[str | None] = mapped_column(
|
||||
ForeignKey("addresses_contact_postal_addresses.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
target_kind: Mapped[str] = mapped_column(String(30), default="contact", nullable=False, index=True)
|
||||
label: Mapped[str | None] = mapped_column(String(255))
|
||||
order_index: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
metadata_: Mapped[dict[str, Any] | None] = mapped_column("metadata", JSON, nullable=True)
|
||||
|
||||
address_list: Mapped[AddressList] = relationship(back_populates="entries")
|
||||
contact: Mapped[Contact] = relationship(back_populates="address_list_entries")
|
||||
contact_email: Mapped[ContactEmail | None] = relationship(back_populates="address_list_entries")
|
||||
contact_postal_address: Mapped[ContactPostalAddress | None] = relationship(back_populates="address_list_entries")
|
||||
|
||||
|
||||
class AddressSyncSource(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_sync_sources"
|
||||
__table_args__ = (
|
||||
Index("ix_addresses_sync_sources_book_status", "address_book_id", "status"),
|
||||
Index("ix_addresses_sync_sources_connector", "tenant_id", "connector_type"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
address_book_id: Mapped[str] = mapped_column(ForeignKey("addresses_address_books.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
connector_type: Mapped[str] = mapped_column(String(60), nullable=False, index=True)
|
||||
display_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
external_account_ref: Mapped[str | None] = mapped_column(String(1000), nullable=True)
|
||||
external_address_book_ref: Mapped[str | None] = mapped_column(String(1000), nullable=True)
|
||||
sync_direction: Mapped[str] = mapped_column(String(30), default="read_only", nullable=False, index=True)
|
||||
read_only: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
|
||||
status: Mapped[str] = mapped_column(String(30), default="idle", nullable=False, index=True)
|
||||
sync_token: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
etag: Mapped[str | None] = mapped_column(String(1000), nullable=True)
|
||||
remote_revision: Mapped[str | None] = mapped_column(String(1000), nullable=True)
|
||||
last_attempted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
last_success_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
last_diagnostic: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
|
||||
created_by_account_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
updated_by_account_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
metadata_: Mapped[dict[str, Any] | None] = mapped_column("metadata", JSON, nullable=True)
|
||||
|
||||
address_book: Mapped[AddressBook] = relationship(back_populates="sync_sources")
|
||||
tombstones: Mapped[list["AddressSyncTombstone"]] = relationship(back_populates="sync_source", cascade="all, delete-orphan")
|
||||
conflicts: Mapped[list["AddressSyncConflict"]] = relationship(back_populates="sync_source", cascade="all, delete-orphan")
|
||||
diagnostics: Mapped[list["AddressSyncDiagnostic"]] = relationship(back_populates="sync_source", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class AddressSyncTombstone(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_sync_tombstones"
|
||||
__table_args__ = (
|
||||
Index("ix_addresses_sync_tombstones_source_remote", "sync_source_id", "remote_uid"),
|
||||
Index("ix_addresses_sync_tombstones_source_href", "sync_source_id", "resource_href"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
sync_source_id: Mapped[str] = mapped_column(ForeignKey("addresses_sync_sources.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
address_book_id: Mapped[str] = mapped_column(ForeignKey("addresses_address_books.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
contact_id: Mapped[str | None] = mapped_column(ForeignKey("addresses_contacts.id", ondelete="SET NULL"), nullable=True, index=True)
|
||||
remote_uid: Mapped[str | None] = mapped_column(String(1000), nullable=True)
|
||||
resource_href: Mapped[str | None] = mapped_column(String(1000), nullable=True)
|
||||
local_deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
remote_deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
synced_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
metadata_: Mapped[dict[str, Any] | None] = mapped_column("metadata", JSON, nullable=True)
|
||||
|
||||
sync_source: Mapped[AddressSyncSource] = relationship(back_populates="tombstones")
|
||||
address_book: Mapped[AddressBook] = relationship()
|
||||
contact: Mapped[Contact | None] = relationship()
|
||||
|
||||
|
||||
class AddressSyncConflict(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_sync_conflicts"
|
||||
__table_args__ = (
|
||||
Index("ix_addresses_sync_conflicts_source_status", "sync_source_id", "status"),
|
||||
Index("ix_addresses_sync_conflicts_contact_status", "contact_id", "status"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
sync_source_id: Mapped[str] = mapped_column(ForeignKey("addresses_sync_sources.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
address_book_id: Mapped[str] = mapped_column(ForeignKey("addresses_address_books.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
contact_id: Mapped[str | None] = mapped_column(ForeignKey("addresses_contacts.id", ondelete="SET NULL"), nullable=True, index=True)
|
||||
remote_uid: Mapped[str | None] = mapped_column(String(1000), nullable=True)
|
||||
resource_href: Mapped[str | None] = mapped_column(String(1000), nullable=True)
|
||||
field_path: Mapped[str] = mapped_column(String(500), nullable=False)
|
||||
local_value: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
|
||||
remote_value: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
|
||||
local_updated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
remote_updated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
status: Mapped[str] = mapped_column(String(30), default="open", nullable=False, index=True)
|
||||
resolution: Mapped[str | None] = mapped_column(String(60), nullable=True)
|
||||
resolved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
resolved_by_account_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
metadata_: Mapped[dict[str, Any] | None] = mapped_column("metadata", JSON, nullable=True)
|
||||
|
||||
sync_source: Mapped[AddressSyncSource] = relationship(back_populates="conflicts")
|
||||
address_book: Mapped[AddressBook] = relationship()
|
||||
contact: Mapped[Contact | None] = relationship()
|
||||
|
||||
|
||||
class AddressSyncDiagnostic(Base, TimestampMixin):
|
||||
__tablename__ = "addresses_sync_diagnostics"
|
||||
__table_args__ = (
|
||||
Index("ix_addresses_sync_diagnostics_source_created", "sync_source_id", "created_at"),
|
||||
Index("ix_addresses_sync_diagnostics_source_severity", "sync_source_id", "severity"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
||||
tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True)
|
||||
sync_source_id: Mapped[str] = mapped_column(ForeignKey("addresses_sync_sources.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
severity: Mapped[str] = mapped_column(String(30), default="info", nullable=False, index=True)
|
||||
code: Mapped[str] = mapped_column(String(120), nullable=False, index=True)
|
||||
message: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
details: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
|
||||
|
||||
sync_source: Mapped[AddressSyncSource] = relationship(back_populates="diagnostics")
|
||||
|
||||
|
||||
__all__ = [
|
||||
"AddressBook",
|
||||
"AddressList",
|
||||
"AddressListEntry",
|
||||
"AddressSyncConflict",
|
||||
"AddressSyncDiagnostic",
|
||||
"AddressSyncSource",
|
||||
"AddressSyncTombstone",
|
||||
"Contact",
|
||||
"ContactEmail",
|
||||
"ContactPhone",
|
||||
"ContactPostalAddress",
|
||||
"new_uuid",
|
||||
]
|
||||
Reference in New Issue
Block a user