angee.posts.models
Source models for the posts addon — public feeds, engagement, and following.
Posts is the public-post surface layered on messaging. It reuses the one idempotent Message.objects.ingest write path (a public post is a messaging.Message in a messaging.Thread) and adds the posts overlay:
- :class:
Feed— anintegrate.Integrationchild +Bridge(exactly likemessaging.Channel) that polls an external platform for public posts; itsFeedBackenddoes the transport+parse, andsync()maps each post onto the messaging ingest, then overlays engagement. - :class:
FeedFollow— the following / timeline subscription edge. - :class:
PostMetrics— rolled-up public engagement counts for a message. - per-actor post reactions (like / repost / emoji) reuse the single
messaging.Reactiontable — posts writeslike/repostas reaction values on the sharedmessaging.Messagerather than owning a parallel table. - :class:
Quota— the per-integration API-unit ledger feed backends spend. - :class:
ThreadPublic/ :class:MessagePublic— the public-thread fields posts contributes ontomessaging.Thread/messaging.Messagethrough the same-rowextendsseam.
The dependency points one way (posts → messaging → parties/integrate/storage); posts never edits or forks messaging.
Feed
class Feed(Bridge)A connected public-content source that polls an external platform for posts.
An integrate.Integration child (identity / credential / status / owner from the connection substrate) and a Bridge (the scheduler + run_sync drive it through sync; integrate.scheduler.run_due_bridges auto-discovers any concrete Bridge subclass, so no registration is needed). backend_class selects the platform — youtube / facebook are contributed by downstream posts_integrate_* addons; manual is the neutral null-object.
A paused feed carries a NULL next_sync_at (not scheduled); activating it schedules the first poll. handle is the parties.Handle the feed monitors and posts as (its OAuth token lives on the handle / the integration credential).
backend_class
Registry key for the feed backend bound to this feed.
external_id
The external channel/page/account id this feed follows on its platform.
Meta
class Meta()Django model options for the feed child model.
backend
@property
def backend() -> FeedBackendReturn this feed's selected backend, bound to this row.
sync
def sync() -> intFetch new posts, ingest their message core, and overlay engagement.
The message core (thread/message/parts) is the messaging owner's job, so a public post shares email's one idempotent write path; posts only writes the overlay it owns (public payload / metrics / reactions / post edges). The ingest is told the structural facts a public feed differs on: every thread is born a PUBLIC_THREAD with PUBLIC visibility — the ingest owner derives the COMMENT kind from that shape — and the RFC-5322 quotation builder is skipped (quote_edges=False) so a post's short shared text does not mint spurious email quote edges.
FeedFollow
class FeedFollow(SqidMixin, AuditMixin, AngeeModel)A follow of a :class:Feed by a parties.Handle — the timeline subscription.
The following edge behind a public timeline: a handle subscribes to a feed's posts. ended_at closes a follow (an open/closed interval), so unfollowing is an update, not a delete, and the history is retained. The timeline itself is the derived join FeedFollow → Feed → Thread → Message (a downstream query owner).
Meta
class Meta()Django model options for the feed-follow source model.
__str__
def __str__() -> strReturn a readable follow label for Django displays.
PostMetrics
class PostMetrics(SqidMixin, AuditMixin, AngeeModel)Rolled-up public engagement counts for one message (the platform snapshot).
Flat one-to-one, not MTI — the counter set overlaps heavily across platforms; platform extras go in metadata. Counters are overwritten with the latest platform snapshot (no F() delta), so the feed sync is the single writer.
Meta
class Meta()Django model options for the post-metrics source model.
__str__
def __str__() -> strReturn a readable metrics label for Django displays.
Quota
class Quota(SqidMixin, AuditMixin, AngeeModel)A per-integration API-unit ledger for one billing period.
Feed backends spend platform API units (search, list, insert) against a per-period budget; :meth:~angee.posts.managers.QuotaManager.consume atomically debits this ledger and refuses when the budget is insufficient. Enforcement is cooperative — the backend must ask before it spends.
Meta
class Meta()Django model options for the quota source model.
__str__
def __str__() -> strReturn a readable quota label for Django displays.
ThreadPublic
class ThreadPublic(AngeeModel)Public-post payload posts contributes onto messaging.Thread (same row).
subject_url links a public thread to its post's canonical URL. It has no producer in base messaging, so posts owns it; the structural modality/ visibility discriminators stay owned by messaging.
Meta
class Meta()Abstract same-row extension composed into messaging.Thread.
MessagePublic
class MessagePublic(AngeeModel)Public-post fields posts contributes onto messaging.Message (same row).
is_original_post marks the root post of a public thread (a post with no parent). It has no producer in base messaging, so posts owns it and the composer folds it onto the single messaging.Message table.
Meta
class Meta()Abstract same-row extension composed into messaging.Message.