Commit 87638518 by Serbaf

0.6.1

parent f1913b1b
...@@ -100,3 +100,8 @@ Main differences with last major release: ...@@ -100,3 +100,8 @@ Main differences with last major release:
improvements were added on the last versions of 0.5.x improvements were added on the last versions of 0.5.x
* Tests have been added that cover most of the code in tweets.py and utils.py. * Tests have been added that cover most of the code in tweets.py and utils.py.
In the next versions probably even more will be added. In the next versions probably even more will be added.
Version 0.6.1:
Added more tests and the "as_json()" function divided in two: "as_long_json",
which simply returns the __dict__ of the Tweet instance, with all its key-value
pairs, and "as_short_json", which returns just the non-empty key-value pairs
(and it won't even return nested dicts it they have no content).
...@@ -34,6 +34,7 @@ PyGithub==1.43.5 ...@@ -34,6 +34,7 @@ PyGithub==1.43.5
Pygments==2.3.1 Pygments==2.3.1
PyJWT==1.7.1 PyJWT==1.7.1
pyparsing==2.3.1 pyparsing==2.3.1
pysnooper==0.0.38
python-dateutil==2.8.0 python-dateutil==2.8.0
python-gitlab==1.8.0 python-gitlab==1.8.0
pytz==2018.9 pytz==2018.9
......
...@@ -51,6 +51,6 @@ setup( ...@@ -51,6 +51,6 @@ setup(
test_suite='tests', test_suite='tests',
tests_require=test_requirements, tests_require=test_requirements,
url='https://github.com/Serbaf/tweet_model', url='https://github.com/Serbaf/tweet_model',
version='0.6.0', version='0.6.1',
zip_safe=False, zip_safe=False,
) )
...@@ -9,12 +9,12 @@ import datetime ...@@ -9,12 +9,12 @@ import datetime
################# #################
# FIXTURES # # FIXTURES #
################# #################
@pytest.fixture @pytest.fixture(scope="module")
def empty_tweet(): def empty_tweet():
return Tweet() return Tweet()
@pytest.fixture @pytest.fixture(scope="module")
def tweet1(): def tweet1():
return Tweet(id="128977963458933", return Tweet(id="128977963458933",
text="En un lugar de la Mancha...", text="En un lugar de la Mancha...",
...@@ -22,7 +22,22 @@ def tweet1(): ...@@ -22,7 +22,22 @@ def tweet1():
user__created_at="2019-05-01 23:54:27") user__created_at="2019-05-01 23:54:27")
@pytest.fixture @pytest.fixture(scope="module")
def tweet2(csv_file_path):
"""
Returns a real tweet instantiated from a CSV. For this to work
test_get_tweet_from_csv_raw_line_OK should be OK. The instantiated tweet is
the first in the CSV doc (2nd line, just after the header)
"""
with open(csv_file_path) as file_reader:
header = file_reader.readline()
tweet_contents = file_reader.readline()
tweet = utils.get_tweet_from_csv_raw_line(header, tweet_contents)
return tweet
@pytest.fixture(scope="module")
def dict_tweet1(): def dict_tweet1():
dictionary_tweet1 = {} dictionary_tweet1 = {}
dictionary_tweet1["id"] = 128977963458933 dictionary_tweet1["id"] = 128977963458933
...@@ -35,13 +50,60 @@ def dict_tweet1(): ...@@ -35,13 +50,60 @@ def dict_tweet1():
return dictionary_tweet1 return dictionary_tweet1
@pytest.fixture @pytest.fixture(scope="module")
def dict_tweet2():
"""
Dictionary representation of the same data as the fixture "tweet2"
"""
dictionary_tweet2 = {}
dictionary_tweet2["created_at"] = datetime.datetime(2019, 5, 1, 23, 59, 16)
dictionary_tweet2["favorite_count"] = 0
dictionary_tweet2["id"] = 1123738691938193410
dictionary_tweet2["in_reply_to_screen_name"] = 'leoffmiranda'
dictionary_tweet2["in_reply_to_status_id"] = 1123737830554066945
dictionary_tweet2["in_reply_to_user_id"] = 176244402
dictionary_tweet2["is_quote_status"] = False
dictionary_tweet2["retweet_count"] = 0
dictionary_tweet2["source"] =\
'<a href="http://twitter.com" rel="nofollow">Twitter Web Client</a>'
dictionary_tweet2["text"] = '@leoffmiranda perfeito. Pele ta na categoria Tolkien pra fantasia, pode ate existir escritores de fantasia melhores… https://t.co/sl5Nd82l71'
dictionary_tweet2["truncated"] = True
dictionary_tweet2["entities"] = {}
dictionary_tweet2["entities"]["urls"] = {}
dictionary_tweet2["entities"]["urls"]["expanded_url"] =\
['https://twitter.com/i/web/status/1123738691938193410']
dictionary_tweet2["entities"]["user_mentions"] = {}
dictionary_tweet2["entities"]["user_mentions"]["screen_name"] = ['leoffmiranda']
dictionary_tweet2["user"] = {}
dictionary_tweet2["user"]["created_at"] = datetime.datetime(2009, 8, 24, 20, 21, 5)
dictionary_tweet2["user"]["favourites_count"] = 108774
dictionary_tweet2["user"]["followers_count"] = 4803
dictionary_tweet2["user"]["friends_count"] = 1044
dictionary_tweet2["user"]["geo_enabled"] = True
dictionary_tweet2["user"]["id"] = 68503128
dictionary_tweet2["user"]["lang"] = "pt"
dictionary_tweet2["user"]["listed_count"] = 45
dictionary_tweet2["user"]["location"] = "ArenaCorinthians"
dictionary_tweet2["user"]["name"] = 'Corinthians matou o futebol. #RipFutibas'
dictionary_tweet2["user"]["profile_image_url"] = 'http://pbs.twimg.com/profile_images/1099105765711929345/dz5f_SdP_normal.jpg'
dictionary_tweet2["user"]["screen_name"] = 'tathiane_vidal'
dictionary_tweet2["user"]["statuses_count"] = 177470
dictionary_tweet2["user"]["verified"] = False
return dictionary_tweet2
@pytest.fixture(scope="module")
def csv_file_path(): def csv_file_path():
current_dir = os.path.dirname(os.path.realpath(__file__)) current_dir = os.path.dirname(os.path.realpath(__file__))
return os.path.join(current_dir, "resources", "01.csv") return os.path.join(current_dir, "resources", "01.csv")
@pytest.fixture @pytest.fixture(scope="module")
def fake_file_path(): def fake_file_path():
current_dir = os.path.dirname(os.path.realpath(__file__)) current_dir = os.path.dirname(os.path.realpath(__file__))
return os.path.join(current_dir, "resources", "false.csv") return os.path.join(current_dir, "resources", "false.csv")
...@@ -96,6 +158,40 @@ class TestTweetObjects: ...@@ -96,6 +158,40 @@ class TestTweetObjects:
assert new_dict == {"id": 128977963458933, assert new_dict == {"id": 128977963458933,
"text": "En un lugar de la Mancha..."} "text": "En un lugar de la Mancha..."}
def test_tweet_list_fields_OK(self, tweet2):
"""
Check that the list fields in the instantiated tweets work as expected
"""
assert tweet2["entities"]["user_mentions"]["screen_name"] == \
["leoffmiranda"]
def test_as_short_json_tweet1_OK(self, tweet1):
"""
Check that the function "as_short_json" returns a correct
representation of the tweet in dict form and with just the non-empty
fields
"""
result = {}
result["id"] = 128977963458933
result["text"] = "En un lugar de la Mancha..."
result["user"] = {}
result["user"]["name"] = "Julio César"
result["user"]["created_at"] =\
datetime.datetime(2019, 5, 1, 23, 54, 27)
assert result == tweet1.as_short_json()
def test_as_short_json_tweet2_OK(self, tweet2, dict_tweet2):
"""
Check that the function "as_short_json" returns a correct
representation of the tweet2 in dict form and with just the non-empty
fields
"""
assert tweet2.as_short_json() == dict_tweet2
class TestTweetUtils: class TestTweetUtils:
def test_get_tweet_from_csv_raw_line_OK(self, csv_file_path): def test_get_tweet_from_csv_raw_line_OK(self, csv_file_path):
......
...@@ -144,47 +144,52 @@ class Tweet(): ...@@ -144,47 +144,52 @@ class Tweet():
try: try:
self.created_at =\ self.created_at =\
datetime.datetime.strptime(created_at, "%Y-%m-%d %H:%M:%S") datetime.datetime.strptime(created_at, "%Y-%m-%d %H:%M:%S")
except (TypeError, ValueError): except Exception as exc:
self.created_at = created_at self.created_at = created_at
try: try:
self.id = int(id) self.id = int(id)
except TypeError: except Exception as exc:
self.id = id self.id = id
try: if type(truncated) is str:
self.truncated = bool(truncated) if truncated == "True":
except TypeError: self.truncated = True
elif truncated == "False":
self.truncated = False
else:
self.truncated = truncated self.truncated = truncated
try: try:
self.in_reply_to_status_id = int(in_reply_to_status_id) self.in_reply_to_status_id = int(in_reply_to_status_id)
except TypeError: except Exception as exc:
self.in_reply_to_status_id = in_reply_to_status_id self.in_reply_to_status_id = in_reply_to_status_id
try: try:
self.in_reply_to_user_id = int(in_reply_to_user_id) self.in_reply_to_user_id = int(in_reply_to_user_id)
except TypeError: except Exception as exc:
self.in_reply_to_user_id = in_reply_to_user_id self.in_reply_to_user_id = in_reply_to_user_id
try: try:
self.quoted_status_id = int(quoted_status_id) self.quoted_status_id = int(quoted_status_id)
except TypeError: except Exception as exc:
self.quoted_status_id = quoted_status_id self.quoted_status_id = quoted_status_id
try: if type(is_quote_status) is str:
self.is_quote_status = bool(is_quote_status) if is_quote_status == "True":
except TypeError: self.is_quote_status = True
elif is_quote_status == "False":
self.is_quote_status = False
else:
self.is_quote_status = is_quote_status self.is_quote_status = is_quote_status
try: try:
self.retweet_count = int(retweet_count) self.retweet_count = int(retweet_count)
except TypeError: except Exception as exc:
self.retweet_count = retweet_count self.retweet_count = retweet_count
try: try:
self.favorite_count = int(favorite_count) self.favorite_count = int(favorite_count)
except TypeError: except Exception as exc:
self.favorite_count = favorite_count self.favorite_count = favorite_count
self.text = text self.text = text
...@@ -216,48 +221,54 @@ class Tweet(): ...@@ -216,48 +221,54 @@ class Tweet():
try: try:
self.user["id"] = int(user__id) self.user["id"] = int(user__id)
except TypeError: except Exception as exc:
self.user["id"] = user__id self.user["id"] = user__id
try: try:
self.user["created_at"] = datetime.datetime.strptime( self.user["created_at"] = datetime.datetime.strptime(
user__created_at, "%Y-%m-%d %H:%M:%S") user__created_at, "%Y-%m-%d %H:%M:%S")
except (TypeError, ValueError): except Exception as exc:
self.user["created_at"] = user__created_at self.user["created_at"] = user__created_at
try: if type(user__verified) is str:
self.user["verified"] = bool(user__verified) if user__verified == "True":
except TypeError: self.user["verified"] = True
elif user__verified == "False":
self.user["verified"] = False
else:
self.user["verified"] = user__verified self.user["verified"] = user__verified
try: try:
self.user["followers_count"] = int(user__followers_count) self.user["followers_count"] = int(user__followers_count)
except TypeError: except Exception as exc:
self.user["followers_count"] = user__followers_count self.user["followers_count"] = user__followers_count
try: try:
self.user["friends_count"] = int(user__friends_count) self.user["friends_count"] = int(user__friends_count)
except TypeError: except Exception as exc:
self.user["friends_count"] = user__friends_count self.user["friends_count"] = user__friends_count
try: try:
self.user["listed_count"] = int(user__listed_count) self.user["listed_count"] = int(user__listed_count)
except TypeError: except Exception as exc:
self.user["listed_count"] = user__listed_count self.user["listed_count"] = user__listed_count
try: try:
self.user["favourites_count"] = int(user__favourites_count) self.user["favourites_count"] = int(user__favourites_count)
except TypeError: except Exception as exc:
self.user["favourites_count"] = user__favourites_count self.user["favourites_count"] = user__favourites_count
try: try:
self.user["statuses_count"] = int(user__statuses_count) self.user["statuses_count"] = int(user__statuses_count)
except TypeError: except Exception as exc:
self.user["statuses_count"] = user__statuses_count self.user["statuses_count"] = user__statuses_count
try: if type(user__geo_enabled) is str:
self.user["geo_enabled"] = bool(user__geo_enabled) if user__geo_enabled == "True":
except TypeError: self.user["geo_enabled"] = True
elif user__geo_enabled == "False":
self.user["geo_enabled"] = False
else:
self.user["geo_enabled"] = user__geo_enabled self.user["geo_enabled"] = user__geo_enabled
self.user["name"] = user__name self.user["name"] = user__name
...@@ -305,7 +316,7 @@ class Tweet(): ...@@ -305,7 +316,7 @@ class Tweet():
self.coordinates["coordinates"] =\ self.coordinates["coordinates"] =\
[float(coords) [float(coords)
for coords in json.loads(coordinates__coordinates)] for coords in json.loads(coordinates__coordinates)]
except (TypeError, IndexError): except Exception as exc:
self.coordinates["coordinates"] = coordinates__coordinates self.coordinates["coordinates"] = coordinates__coordinates
# Place object # Place object
...@@ -328,7 +339,7 @@ class Tweet(): ...@@ -328,7 +339,7 @@ class Tweet():
for y in range(len(coords[x]))] for y in range(len(coords[x]))]
for x in range(len(coords))] for x in range(len(coords))]
except (TypeError, IndexError): except Exception as exc:
self.place["bounding_box"]["coordinates"] =\ self.place["bounding_box"]["coordinates"] =\
place__bounding_box__coordinates place__bounding_box__coordinates
self.place["bounding_box"]["type"] = place__bounding_box__type self.place["bounding_box"]["type"] = place__bounding_box__type
...@@ -338,8 +349,9 @@ class Tweet(): ...@@ -338,8 +349,9 @@ class Tweet():
# Entities hashtags # Entities hashtags
self.entities["hashtags"] = {} self.entities["hashtags"] = {}
try: try:
self.entities["hashtags"]["text"] = list(entities__hashtags__text) self.entities["hashtags"]["text"] =\
except TypeError: json.loads(entities__hashtags__text)
except Exception as exc:
self.entities["hashtags"]["text"] = entities__hashtags__text self.entities["hashtags"]["text"] = entities__hashtags__text
self.entities["hashtags"]["indices"] = entities__hashtags__indices self.entities["hashtags"]["indices"] = entities__hashtags__indices
...@@ -347,8 +359,8 @@ class Tweet(): ...@@ -347,8 +359,8 @@ class Tweet():
self.entities["media"] = {} self.entities["media"] = {}
try: try:
self.entities["media"]["media_url"] =\ self.entities["media"]["media_url"] =\
list(entities__media__media_url) json.loads(entities__media__media_url)
except TypeError: except Exception as exc:
self.entities["media"]["media_url"] = entities__media__media_url self.entities["media"]["media_url"] = entities__media__media_url
self.entities["media"]["display_url"] = entities__media__display_url self.entities["media"]["display_url"] = entities__media__display_url
self.entities["media"]["expanded_url"] = entities__media__expanded_url self.entities["media"]["expanded_url"] = entities__media__expanded_url
...@@ -397,8 +409,8 @@ class Tweet(): ...@@ -397,8 +409,8 @@ class Tweet():
self.entities["urls"] = {} self.entities["urls"] = {}
try: try:
self.entities["urls"]["expanded_url"] =\ self.entities["urls"]["expanded_url"] =\
list(entities__urls__expanded_url) json.loads(entities__urls__expanded_url)
except TypeError: except Exception as exc:
self.entities["urls"]["expanded_url"] =\ self.entities["urls"]["expanded_url"] =\
entities__urls__expanded_url entities__urls__expanded_url
self.entities["urls"]["display_url"] = entities__urls__display_url self.entities["urls"]["display_url"] = entities__urls__display_url
...@@ -417,8 +429,8 @@ class Tweet(): ...@@ -417,8 +429,8 @@ class Tweet():
self.entities["user_mentions"] = {} self.entities["user_mentions"] = {}
try: try:
self.entities["user_mentions"]["screen_name"] =\ self.entities["user_mentions"]["screen_name"] =\
list(entities__user_mentions__screen_name) json.loads(entities__user_mentions__screen_name)
except TypeError: except Exception as exc:
self.entities["user_mentions"]["screen_name"] =\ self.entities["user_mentions"]["screen_name"] =\
entities__user_mentions__screen_name entities__user_mentions__screen_name
self.entities["user_mentions"]["id"] = entities__user_mentions__id self.entities["user_mentions"]["id"] = entities__user_mentions__id
...@@ -494,18 +506,44 @@ class Tweet(): ...@@ -494,18 +506,44 @@ class Tweet():
pass pass
return tweet_subset return tweet_subset
def as_json(self) -> Dict: def as_short_json(self, dictionary: Dict=None) -> Dict:
""" """
Return the Tweet object in a JSON-like representation (nested dicts) Return the Tweet object in a short JSON-like representation
but without all the null key-value pairs
""" """
json_tweet = {} if dictionary is None:
json_tweet = {}
for key, value in self.__dict__.items(): for key, value in self.__dict__.items():
if value is not None: if type(value) is dict:
json_tweet[key] = value nested_dict = self.as_short_json(value)
if nested_dict is not None:
json_tweet[key] = nested_dict
elif value is not None:
json_tweet[key] = value
return json_tweet
else:
dictie = {}
for key, value in dictionary.items():
if type(value) is dict:
nested_dict = self.as_short_json(value)
if nested_dict is not None:
dictie[key] = nested_dict
elif value is not None:
dictie[key] = value
if len(dictie) == 0:
return None
else:
return dictie
def as_long_json(self) -> Dict:
"""
Return the Tweet object in a JSON-like representation (nested dicts).
Just the __dict__ of the class
"""
return json_tweet return self.__dict__
def __getitem__(self, key): def __getitem__(self, key):
return getattr(self, key) return getattr(self, key)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment