Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ except FopostError as err:
| Namespace | Methods |
| ------------ | -------------------------------------------------------------------------------------- |
| `posts` | `list`, `iter`, `iter_pages`, `get`, `create`, `update`, `delete`, `publish`, `cancel`, `retry`, `preflight`, `deliveries` |
| `accounts` | `list`, `get`, `health`, `update`, `move`, `create_telegram_connect_code`, `get_telegram_connect_status`, `get_telegram_bot_commands`, `set_telegram_bot_commands`, `delete_telegram_bot_commands`, `list_slack_channels`, `list_slack_members`, `get_slack_identity`, `update_slack_identity`, `get_ice_breakers`, `set_ice_breakers`, `delete_ice_breakers`, `get_persistent_menu`, `set_persistent_menu`, `delete_persistent_menu`, `get_greeting`, `set_greeting`, `delete_greeting`, `get_webhook_subscription`, `resubscribe_webhook`, `list_discord_channels`, `switch_discord_channel`, `get_discord_identity`, `update_discord_identity`, `list_discord_pins`, `delete_discord_message`, `pin_discord_message`, `unpin_discord_message`, `crosspost_discord_message`, `create_discord_thread`, `send_discord_dm`, `list_discord_events`, `get_discord_event`, `create_discord_event`, `update_discord_event`, `delete_discord_event`, `list_discord_members`, `get_discord_member`, `list_discord_roles`, `create_discord_role`, `update_discord_role`, `delete_discord_role`, `add_discord_member_role`, `remove_discord_member_role` |
| `accounts` | `list`, `get`, `health`, `update`, `move`, `create_telegram_connect_code`, `get_telegram_connect_status`, `get_telegram_bot_commands`, `set_telegram_bot_commands`, `delete_telegram_bot_commands`, `list_slack_channels`, `list_slack_members`, `get_slack_identity`, `update_slack_identity`, `get_ice_breakers`, `set_ice_breakers`, `delete_ice_breakers`, `get_persistent_menu`, `set_persistent_menu`, `delete_persistent_menu`, `get_greeting`, `set_greeting`, `delete_greeting`, `get_webhook_subscription`, `resubscribe_webhook`, `list_discord_channels`, `switch_discord_channel`, `get_discord_identity`, `update_discord_identity`, `list_discord_pins`, `delete_discord_message`, `pin_discord_message`, `unpin_discord_message`, `crosspost_discord_message`, `create_discord_thread`, `send_discord_dm`, `list_discord_events`, `get_discord_event`, `create_discord_event`, `update_discord_event`, `delete_discord_event`, `list_discord_members`, `get_discord_member`, `list_discord_roles`, `create_discord_role`, `update_discord_role`, `delete_discord_role`, `add_discord_member_role`, `remove_discord_member_role`, `platform_metrics` |
| `account_groups` | `list`, `create`, `get`, `update`, `delete`, `set_members` |
| `workspaces` | `list`, `get` |
| `labels` | `list` |
Expand Down
47 changes: 35 additions & 12 deletions src/fopost/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
POST_STATUSES,
AccountGroup,
AccountMove,
AccountPlatformMetrics,
AccountRename,
ActivityActor,
ActivityEvent,
Expand Down Expand Up @@ -139,6 +140,8 @@
PageMeta,
PinterestBoard,
Platform,
PlatformMetricRow,
PlatformMetricsBlock,
Post,
PostAccount,
PostStatus,
Expand Down Expand Up @@ -184,29 +187,38 @@
FoPost = Fopost

__all__ = [
"DEFAULT_BASE_URL",
"PLATFORMS",
"POST_STATUSES",
"AccountGroup",
"AccountMove",
"AccountPlatformMetrics",
"AccountRename",
"ActivityActor",
"ActivityEvent",
"ActivityPage",
"Ad",
"AdAccountTree",
"AdCampaign",
"AdCampaignNode",
"AdConnection",
"AdCreative",
"AdInsights",
"AdInsightsReport",
"AdSet",
"AdSetNode",
"AdSource",
"AiCreditBalance",
"AiCredits",
"Audience",
"AudiencesResult",
"AuthenticationError",
"BoostablePost",
"BulkAdStatusResult",
"CaptionResult",
"ContentBlock",
"ContentSignal",
"DEFAULT_BASE_URL",
"Delivery",
"ExternalAd",
"FeedLead",
"FoPost",
"Fopost",
"FopostError",
Expand Down Expand Up @@ -242,23 +254,35 @@
"InboxReplyResult",
"InboxStartConversationResult",
"InboxThread",
"InsightsMetrics",
"InsightsRow",
"Label",
"Lead",
"LeadForm",
"LeadFormDetail",
"LeadFormSource",
"LeadPage",
"LeadPageSubscription",
"LeadsFeedPage",
"LeadsPage",
"MediaItem",
"NetworkAd",
"NotFoundError",
"PLATFORMS",
"POST_STATUSES",
"Page",
"PageMeta",
"PaymentRequiredError",
"PermissionDeniedError",
"Platform",
"PlatformMetricRow",
"PlatformMetricsBlock",
"Post",
"PostAccount",
"PostStatus",
"PresignedUpload",
"RateLimitError",
"ReachEstimate",
"RepurposeResult",
"RewriteResult",
"RewriteVariant",
Expand Down Expand Up @@ -306,7 +330,6 @@
"ValidateMediaResult",
"ValidatePlatformCheck",
"ValidatePostResult",
"ContentSignal",
"Workspace",
"__version__",
"AdAccountTree",
Expand All @@ -318,14 +341,6 @@
"AdSetNode",
"BulkAdStatusResult",
"FeedLead",
"InsightsMetrics",
"InsightsRow",
"LeadFormDetail",
"LeadPage",
"LeadPageSubscription",
"LeadsFeedPage",
"NetworkAd",
"ReachEstimate",
"GoogleAdScheduleSlot",
"GoogleAsset",
"GoogleAssetGroup",
Expand All @@ -338,4 +353,12 @@
"GoogleLocalServicesLead",
"GoogleSearchTerm",
"GoogleSharedSet",
"InsightsMetrics",
"InsightsRow",
"LeadFormDetail",
"LeadPage",
"LeadPageSubscription",
"LeadsFeedPage",
"NetworkAd",
"ReachEstimate",
]
86 changes: 67 additions & 19 deletions src/fopost/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,39 @@
from pydantic import AliasChoices, AliasGenerator, BaseModel, ConfigDict

__all__ = [
"PLATFORMS",
"POST_STATUSES",
"Platform",
"PostStatus",
"AccountGroup",
"AccountMove",
"AccountPlatformMetrics",
"ACTIVITY_KINDS",
"ActivityKind",
"ActivityActor",
"ActivityEvent",
"ActivityKind",
"ActivityPage",
"AccountRename",
"Ad",
"AdAccountTree",
"AdCampaign",
"AdCampaignNode",
"AdConnection",
"AdCreative",
"AdInsights",
"AdInsightsReport",
"AdSet",
"AdSetNode",
"AdSource",
"AiCreditBalance",
"AiCredits",
"Audience",
"AudiencesResult",
"BoostablePost",
"BulkAdStatusResult",
"CaptionResult",
"ContentBlock",
"ContentSignal",
"Delivery",
"ExternalAd",
"FeedLead",
"FopostModel",
"InboxAccount",
"InboxAccountRef",
"InboxApproval",
Expand All @@ -51,18 +59,31 @@
"InboxReplyResult",
"InboxStartConversationResult",
"InboxThread",
"InsightsMetrics",
"InsightsRow",
"Label",
"Lead",
"LeadForm",
"LeadFormDetail",
"LeadFormSource",
"LeadPage",
"LeadPageSubscription",
"LeadsFeedPage",
"LeadsPage",
"MediaItem",
"FopostModel",
"NetworkAd",
"PLATFORMS",
"POST_STATUSES",
"Page",
"PageMeta",
"Platform",
"PlatformMetricRow",
"PlatformMetricsBlock",
"Post",
"PostAccount",
"PostStatus",
"PresignedUpload",
"ReachEstimate",
"RepurposeResult",
"RewriteResult",
"RewriteVariant",
Expand All @@ -87,45 +108,44 @@
"ValidateMediaResult",
"ValidatePlatformCheck",
"ValidatePostResult",
"ContentSignal",
"Workspace",
"AdAccountTree",
"AdActivity",
"AdActivityResult",
"AdCampaign",
"AdCampaignNode",
"AdCreative",
"AdInsightsReport",
"AdLabel",
"AdLibraryEntry",
"AdLibraryPage",
"AdSet",
"AdSetNode",
"AdStudy",
"BulkAdStatusResult",
"CatalogBatchResult",
"CatalogProduct",
"CatalogProductsPage",
"FeedLead",
"HighDemandPeriod",
"InsightsMetrics",
"InsightsRow",
"IosCampaignLimits",
"LeadFormDetail",
"LeadPage",
"LeadPageSubscription",
"LeadsFeedPage",
"NetworkAd",
"AdActivity",
"AdActivityResult",
"AdLabel",
"AdLibraryEntry",
"AdLibraryPage",
"AdStudy",
"CatalogBatchResult",
"CatalogProduct",
"CatalogProductsPage",
"HighDemandPeriod",
"IosCampaignLimits",
"PartnershipCreator",
"ProductCatalog",
"ProductCatalogsResult",
"ProductFeed",
"ProductFeedUpload",
"ProductSet",
"ReachEstimate",
"ReachFrequencyPrediction",
"ReachFrequencyResult",
"ValueRuleSet",
"ReachEstimate",
]

#: Every platform the API can publish to. Model fields stay plain `str`, so a
Expand Down Expand Up @@ -693,6 +713,34 @@ class DiscordRole(FopostModel):
permissions: str = "0"


class PlatformMetricRow(FopostModel):
"""One metric a network reports under its own name."""

#: The platform's own metric name. Stable — read this, not ``label``.
key: str
#: Ours, and subject to rewording.
label: str
kind: Literal["count", "duration_ms", "currency_usd", "ratio", "series"] | str
#: A number for every kind but ``series``, which is a list of points.
value: Any = None


class PlatformMetricsBlock(FopostModel):
"""One side of the set: the account itself, or its newest measured post."""

fetched_at: str | None = None
external_post_id: str | None = None
metrics: list[PlatformMetricRow] = []


class AccountPlatformMetrics(FopostModel):
"""What only this network reports, in its own vocabulary."""

platform: str
account: PlatformMetricsBlock
post: PlatformMetricsBlock


class Workspace(FopostModel):
id: str
name: str
Expand Down
15 changes: 15 additions & 0 deletions src/fopost/resources/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .._http import unwrap
from ..models import (
AccountMove,
AccountPlatformMetrics,
AccountRename,
BlueskyLanguages,
DiscordChannel,
Expand Down Expand Up @@ -81,6 +82,20 @@ def health(self, account_id: str) -> dict[str, Any]:
body = unwrap(self._http.get(f"/accounts/{account_id}/health"))
return body if isinstance(body, dict) else {"data": body}

def platform_metrics(self, account_id: str) -> AccountPlatformMetrics:
"""The numbers only this account's network reports, in its own vocabulary.

Ad-break earnings, story taps, a retention curve, the search terms behind a
listing — keyed by the platform's own metric names, read from the newest
collected snapshot rather than fetched live. Needs the ``analytics`` scope.

A network whose metric access has not been granted yet answers ``503``
(``platform_metrics_unavailable``) rather than an empty set.
"""
return AccountPlatformMetrics.model_validate(
unwrap(self._http.get(f"/accounts/{account_id}/insights", {"raw": "true"}))
)

def update(self, account_id: str, *, display_name: str | None) -> AccountRename:
"""Rename the account; ``None`` or an empty string restores the platform name."""
return AccountRename.model_validate(
Expand Down
Loading
Loading