зеркало из
https://github.com/viginum-datalab/twscrape.git
synced 2025-10-29 05:04:22 +02:00
add missing string id representation to models #116
Этот коммит содержится в:
родитель
748ceae053
Коммит
277ebd1649
Двоичные данные
.github/example.png
поставляемый
Обычный файл
Двоичные данные
.github/example.png
поставляемый
Обычный файл
Двоичный файл не отображается.
|
После Ширина: | Высота: | Размер: 218 KiB |
@ -13,7 +13,7 @@
|
||||
Twitter GraphQL API implementation with [SNScrape](https://github.com/JustAnotherArchivist/snscrape) data models.
|
||||
|
||||
<div align="center">
|
||||
<img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*0erkeMBhl_qqRofIeU5jMQ.png" alt="example of cli usage" width="560px">
|
||||
<img src=".github/example.png" alt="example of cli usage" width="560px">
|
||||
</div>
|
||||
|
||||
## Install
|
||||
|
||||
@ -4,7 +4,7 @@ from typing import Callable
|
||||
|
||||
from twscrape import API, gather
|
||||
from twscrape.logger import set_log_level
|
||||
from twscrape.models import Tweet, User, parse_tweet
|
||||
from twscrape.models import Tweet, User, UserRef, parse_tweet
|
||||
|
||||
BASE_DIR = os.path.dirname(__file__)
|
||||
DATA_DIR = os.path.join(BASE_DIR, "mocked-data")
|
||||
@ -49,18 +49,33 @@ def mock_rep(fn: Callable, filename: str, as_generator=False):
|
||||
|
||||
def check_tweet(doc: Tweet | None):
|
||||
assert doc is not None
|
||||
assert doc.id is not None
|
||||
assert doc.id_str is not None
|
||||
assert isinstance(doc.id, int)
|
||||
assert isinstance(doc.id_str, str)
|
||||
assert doc.id == int(doc.id_str)
|
||||
assert str(doc.id) == doc.id_str
|
||||
|
||||
assert doc.url is not None
|
||||
assert doc.id_str in doc.url
|
||||
assert doc.user is not None
|
||||
|
||||
assert isinstance(doc.conversationId, int)
|
||||
assert isinstance(doc.conversationIdStr, str)
|
||||
assert str(doc.conversationId) == doc.conversationIdStr
|
||||
|
||||
if doc.inReplyToTweetId is not None:
|
||||
assert isinstance(doc.inReplyToTweetId, int)
|
||||
assert isinstance(doc.inReplyToTweetIdStr, str)
|
||||
assert str(doc.inReplyToTweetId) == doc.inReplyToTweetIdStr
|
||||
|
||||
if doc.inReplyToUser:
|
||||
check_user_ref(doc.inReplyToUser)
|
||||
|
||||
if doc.mentionedUsers:
|
||||
for x in doc.mentionedUsers:
|
||||
check_user_ref(x)
|
||||
|
||||
obj = doc.dict()
|
||||
assert doc.id == obj["id"]
|
||||
assert doc.id_str == obj["id_str"]
|
||||
assert doc.user.id == obj["user"]["id"]
|
||||
|
||||
assert "url" in obj
|
||||
@ -104,10 +119,9 @@ def check_tweet(doc: Tweet | None):
|
||||
|
||||
def check_user(doc: User):
|
||||
assert doc.id is not None
|
||||
assert doc.id_str is not None
|
||||
assert isinstance(doc.id, int)
|
||||
assert isinstance(doc.id_str, str)
|
||||
assert doc.id == int(doc.id_str)
|
||||
assert str(doc.id) == doc.id_str
|
||||
|
||||
assert doc.username is not None
|
||||
assert doc.descriptionLinks is not None
|
||||
@ -127,6 +141,19 @@ def check_user(doc: User):
|
||||
assert str(doc.id) in txt
|
||||
|
||||
|
||||
def check_user_ref(doc: UserRef):
|
||||
assert isinstance(doc.id, int)
|
||||
assert isinstance(doc.id_str, str)
|
||||
assert str(doc.id) == doc.id_str
|
||||
|
||||
assert doc.username is not None
|
||||
assert doc.displayname is not None
|
||||
|
||||
obj = doc.dict()
|
||||
assert doc.id == obj["id"]
|
||||
assert doc.id_str == obj["id_str"]
|
||||
|
||||
|
||||
async def test_search():
|
||||
api = API()
|
||||
mock_rep(api.search_raw, "raw_search", as_generator=True)
|
||||
|
||||
@ -84,13 +84,19 @@ class TextLink(JSONTrait):
|
||||
@dataclass
|
||||
class UserRef(JSONTrait):
|
||||
id: int
|
||||
id_str: str
|
||||
username: str
|
||||
displayname: str
|
||||
_type: str = "snscrape.modules.twitter.UserRef"
|
||||
|
||||
@staticmethod
|
||||
def parse(obj: dict):
|
||||
return UserRef(id=int(obj["id_str"]), username=obj["screen_name"], displayname=obj["name"])
|
||||
return UserRef(
|
||||
id=int(obj["id_str"]),
|
||||
id_str=obj["id_str"],
|
||||
username=obj["screen_name"],
|
||||
displayname=obj["name"],
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -163,6 +169,7 @@ class Tweet(JSONTrait):
|
||||
likeCount: int
|
||||
quoteCount: int
|
||||
conversationId: int
|
||||
conversationIdStr: str
|
||||
hashtags: list[str]
|
||||
cashtags: list[str]
|
||||
mentionedUsers: list[UserRef]
|
||||
@ -173,6 +180,7 @@ class Tweet(JSONTrait):
|
||||
place: Optional[Place] = None
|
||||
coordinates: Optional[Coordinates] = None
|
||||
inReplyToTweetId: int | None = None
|
||||
inReplyToTweetIdStr: str | None = None
|
||||
inReplyToUser: UserRef | None = None
|
||||
source: str | None = None
|
||||
sourceUrl: str | None = None
|
||||
@ -217,6 +225,7 @@ class Tweet(JSONTrait):
|
||||
likeCount=obj["favorite_count"],
|
||||
quoteCount=obj["quote_count"],
|
||||
conversationId=int(obj["conversation_id_str"]),
|
||||
conversationIdStr=obj["conversation_id_str"],
|
||||
hashtags=[x["text"] for x in get_or(obj, "entities.hashtags", [])],
|
||||
cashtags=[x["text"] for x in get_or(obj, "entities.symbols", [])],
|
||||
mentionedUsers=[UserRef.parse(x) for x in get_or(obj, "entities.user_mentions", [])],
|
||||
@ -229,6 +238,7 @@ class Tweet(JSONTrait):
|
||||
place=Place.parse(obj["place"]) if obj.get("place") else None,
|
||||
coordinates=Coordinates.parse(obj),
|
||||
inReplyToTweetId=int_or(obj, "in_reply_to_status_id_str"),
|
||||
inReplyToTweetIdStr=get_or(obj, "in_reply_to_status_id_str"),
|
||||
inReplyToUser=_get_reply_user(obj, res),
|
||||
source=obj.get("source", None),
|
||||
sourceUrl=_get_source_url(obj),
|
||||
|
||||
Загрузка…
x
Ссылка в новой задаче
Block a user