angee.parties.models
Source models for the parties addon.
Parties are the people and organisations a project keeps track of. A party is reached through one or more :class:Handle rows (an email address, a phone number, a social handle) — the same handle that messaging uses as a participant, so this addon is the contacts foundation the messaging addon builds on. The link between a party and a handle is itself confidence-bearing (:class:PartyHandle) so a sync can record an uncertain match as a weak candidate instead of guessing.
Party is a multi-table-inheritance parent; the concrete kind is the child model (:class:Person, :class:Organization), not a column — a person carries name parts and a link to its :class:~angee.iam.models.User, an organisation carries its legal name and domain.
Party
class Party(SqidMixin, AuditMixin, AngeeModel)A person or organisation the project tracks.
The parent owns the common contact identity — the public id, ownership, the display name, avatar, notes, and the lossless-vCard carriers. The concrete kind (and its kind-specific fields) lives on the :class:Person / :class:Organization child row.
Meta
class Meta()Django model options for the party source model.
__str__
def __str__() -> strReturn the party's display name for Django displays.
Person
class Person(AngeeModel)A human party — carries name parts and an optional platform-user link.
Meta
class Meta()Django model options for the person child model.
Organization
class Organization(AngeeModel)An organisation party — carries its legal name and primary domain.
Meta
class Meta()Django model options for the organization child model.
Handle
class Handle(SqidMixin, AuditMixin, AngeeModel)A reachable address or handle of a party on one platform.
Keyed on (platform, value) and, when present, (platform, external_id) — those unique constraints are the ingestion-dedup keys that make re-sync idempotent. party is the resolved owner the :class:PartyHandle manager materialises; it is null until a handle is linked, so a handle synced for an unknown sender is still a valid row.
Platform
class Platform(models.TextChoices)The kind of channel a handle reaches a party through.
Meta
class Meta()Django model options for the handle source model.
__str__
def __str__() -> strReturn the handle value for Django displays.
resolved_confidence
@property
def resolved_confidence() -> float | NoneConfidence of the link that resolved this handle's owner.
party is materialised from the winning :class:PartyHandle (see :meth:PartyHandleManager.resolve) and (party, handle) is unique, so the link matching the resolved party is that winner — its score is the resolution confidence. None when the handle is unowned or, under actor-scoped loading, the resolving link is not readable by the actor.
PartyHandle
class PartyHandle(SqidMixin, AuditMixin, AngeeModel)A confidence-bearing link between a party and one of its handles.
A handle may carry several scored candidate parties, so a sync can surface an uncertain match as a weak link (a conflicting claim is recorded at 0.3 confidence) instead of silently reassigning. The resolved owner is the highest-confidence, non-dismissed link — the value the manager materialises onto :attr:Handle.party.
Source
class Source(models.TextChoices)Where a party↔handle link came from.
Meta
class Meta()Django model options for the party-handle link source model.
__str__
def __str__() -> strReturn a readable link description for Django displays.
Address
class Address(SqidMixin, AuditMixin, AngeeModel)A physical or postal address of a party (the vCard ADR property).
There is intentionally no (party, label) uniqueness — a party may carry two same-labelled addresses — so a CardDAV mapper keys idempotency on the address content, not the label.
Meta
class Meta()Django model options for the address source model.
__str__
def __str__() -> strReturn a one-line address for Django displays.
Affiliation
class Affiliation(SqidMixin, AuditMixin, AngeeModel)A party's membership of an organisation (the vCard ORG/TITLE/ROLE).
The organisation is an organisation-kind :class:Party when known, falling back to a free-text organization_name when the organisation is not itself a tracked party.
Meta
class Meta()Django model options for the affiliation source model.
__str__
def __str__() -> strReturn the affiliation title/org for Django displays.
Folder
class Folder(SqidMixin, AuditMixin, AngeeModel)A group of parties — the local mirror of a synced address book.
The contacts counterpart of storage's Drive/Folder and knowledge's Vault container idea, kept to exactly what sync needs today: the directory it mirrors, the collection source_href (one folder per (directory, source_href) makes the folder upsert idempotent), and the incremental cursors (ctag / sync_token). Owned via created_by; deleting a folder leaves its parties (SET_NULL on :attr:Party.folder). Manual creation and a folder tree (parent) are deferred until a create path lands to exercise them.
Meta
class Meta()Django model options for the folder source model.
__str__
def __str__() -> strReturn the folder name for Django displays.
Directory
class Directory(Bridge)A connected contacts source that syncs parties from an external directory.
An integrate.Integration child (so it draws its credential / owner / status from the connection substrate) and a Bridge (so the scheduler and the eager syncIntegration mutation drive it). backend_class selects the protocol — carddav (contributed by parties_integrate_carddav) — and config carries the source URL. sync() fetches + parses the source, then maps each contact onto the parties managers.
backend_class
Registry key for the directory backend bound to this directory.
Meta
class Meta()Django model options for the directory child model.
backend
@property
def backend() -> DirectoryBackendReturn this directory's selected backend, bound to this row.
sync
def sync() -> intDiscover address books and resolve every contact into parties (the Bridge contract).
Idempotent: each address book mirrors to one :class:Folder (keyed by its source_href), every contact upserts by (folder, source_uid), and a contact that vanished from the source is purged from its folder — so a re-sync converges to the source instead of duplicating it. A collection whose ctag is unchanged is skipped wholesale.