123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- """
- Implementation of the syndication taxonomy in architecture.md
- """
- from dataclasses import dataclass, asdict, replace
- from typing import List, Dict, Optional, Tuple
- @dataclass
- class PublicMetrics:
- reply_count: Optional[int] = None
- quote_count: Optional[int] = None
- retweet_count: Optional[int] = None
- like_count: Optional[int] = None
-
- # may be video only
- view_count: Optional[int] = None
-
-
- def items (self):
- return asdict(self).items()
-
- @dataclass
- class NonPublicMetrics:
- impression_count: Optional[int] = None
- user_profile_clicks: Optional[int] = None
- url_link_clicks: Optional[int] = None
-
- def items (self):
- return asdict(self).items()
- @dataclass
- class MediaItem:
- type: str
- preview_image_url: str
-
- media_key: Optional[str] = None
- image_url: Optional[str] = None
-
- url: Optional[str] = None
- content_type: Optional[str] = None
- duration_ms: Optional[int] = None
-
- height: Optional[int] = None
- width: Optional[int] = None
-
- size: Optional[int] = None
-
- public_metrics: Optional[PublicMetrics] = None
- @dataclass
- class Card:
- display_url: Optional[str] = None
- source_url: Optional[str] = None
- content: Optional[str] = None
- title: Optional[str] = None
- preview_image_url: Optional[str] = None
- image_url: Optional[str] = None
- @dataclass
- class FeedItemAction:
- route: str
- route_params: Dict
- # intended as external resources,
- # not first class display.
- # PDF, chat, embedded image (iframe)
- @dataclass
- class FeedItemAttachment:
- name: str
- content_type: str
-
- url: Optional[str] = None
- content: Optional[object] = None
-
- size: Optional[int] = None
- @dataclass
- class UnrepliedSection:
- feed_item_id: Optional[str] = None
- description: Optional[str] = None
- text: Optional[str] = None
- span: Optional[Tuple[int, int]] = None
-
- def text_from (self, feed_item: 'FeedItem') -> str:
- if not self.span or not feed_item.text:
- return
-
- return feed_item.text[self.span[0]:self.span[1]]
- ReplyingToSection = UnrepliedSection
- @dataclass
- class FeedItem:
- id: str
- created_at: str
-
- display_name: str
- handle: str
-
- published_by: Optional['FeedServiceUser'] = None
-
- text: Optional[str] = None
- html: Optional[str] = None
-
- author_is_verified: Optional[bool] = None
- url: Optional[str] = None
- conversation_id: Optional[str] = None
-
- avi_icon_url: Optional[str] = None
-
- author_url: Optional[str] = None
- author_id: Optional[str] = None
-
- source_url: Optional[str] = None
- source_author_url: Optional[str] = None
-
- reply_depth: Optional[int] = 0
-
- is_marked: Optional[bool] = None
-
- card: Optional[Card] = None
-
- public_metrics: Optional[PublicMetrics] = None
- non_public_metrics: Optional[NonPublicMetrics] = None
-
- retweeted_tweet_id: Optional[str] = None
-
- source_retweeted_by_url: Optional[str] = None
- retweeted_by: Optional[str] = None
- retweeted_by_url: Optional[str] = None
-
- videos: Optional[List[MediaItem]] = None
- photos: Optional[List[MediaItem]] = None
-
- quoted_tweet_id: Optional[str] = None
- quoted_tweet: Optional['FeedItem'] = None
-
- replied_tweet_id: Optional[str] = None
- replied_tweet: Optional['FeedItem'] = None
-
- note: Optional[str] = None
-
- debug_source_data: Optional[Dict] = None
-
- attachments: Optional[List[FeedItemAttachment]] = None
-
- # At some point we may move to feed_item_actions when the set is known
- actions: Optional[Dict[str, FeedItemAction]] = None
-
- # This is a TBD concept to highlight unreplied parts of a message.
- unreplied: Optional[List[UnrepliedSection]] = None
-
- # This is a TBD concept to highlight parts of a message that are a reply.
- replying_to: Optional[List[ReplyingToSection]] = None
- # tm = FeedItem(id="1", text="aa", created_at="fs", display_name="fda", handle="fdsafas")
- @dataclass
- class ThreadItem:
- feed_item: FeedItem
- children: Optional[List['ThreadItem']] = None
- parent: Optional['ThreadItem'] = None
- parents: Optional[List['ThreadItem']] = None
-
- actions: Optional[Dict[str, FeedItemAction]] = None
-
- @dataclass
- class FeedServiceUser:
- id: str
- url: str
- name: str # display_name
- username: str # handle
-
- is_verified: Optional[bool] = None
- is_protected: Optional[bool] = None
-
- created_at: Optional[str] = None
- description: Optional[str] = None
- preview_image_url: Optional[str] = None # deprecated... rename to avatar_image_url
- poster_image_url: Optional[str] = None
- website: Optional[str] = None
- location: Optional[str] = None
-
- actions: Optional[Dict[str, FeedItemAction]] = None
-
- source_url: Optional[str] = None
-
- raw_user: Optional = None
-
- @property
- def display_name (self) -> str:
- return self.name
-
- @property
- def handle (self) -> str:
- return self.username
-
- @property
- def avatar_image_url (self) -> str:
- return self.preview_image_url
- @dataclass
- class CollectionPage:
- """
- Feed is a collection.
- """
-
- id: str
- items: Optional[List[FeedServiceUser|FeedItem|ThreadItem]] = None
- next_token: Optional[str] = None
- last_dt: Optional[str] = None
- def cleandict(d):
- if isinstance(d, dict):
- return {k: cleandict(v) for k, v in d.items() if v is not None}
- elif isinstance(d, list):
- return [cleandict(v) for v in d]
- else:
- return d
|