Skip to content

Commit 9352c43

Browse files
authored
Merge pull request #108 from mobeigi/40-add-support-for-year-of-birth
Fixes #40, #97: Add support for year of birth
2 parents a6f3793 + 7cd0318 commit 9352c43

8 files changed

Lines changed: 73 additions & 46 deletions

File tree

fb2cal/__main__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,9 @@
6767

6868
# Endpoint will return all birthdays for offset_month plus the following 2 consecutive months.
6969
logger.info('Fetching all Birthdays via BirthdayCometRootQuery endpoint...')
70-
# TODO: See #97, offset_month of 10 is needed here because offset months 6/7 are currently returning equivalent months
71-
for offset_month in [0, 3, 6, 9, 10]:
72-
birthday_comet_root_json = facebook_browser.query_graph_ql_birthday_comet_root(offset_month)
73-
facebook_users_for_quarter = transformer.transform_birthday_comet_root_to_birthdays(birthday_comet_root_json)
70+
for offset_month in [0, 3, 6, 9]:
71+
birthday_comet_monthly_json = facebook_browser.query_graph_ql_birthday_comet_monthly(offset_month)
72+
facebook_users_for_quarter = transformer.transform_birthday_comet_monthly_to_birthdays(birthday_comet_monthly_json)
7473
facebook_users.update(facebook_users_for_quarter)
7574

7675
if len(facebook_users) == 0:

fb2cal/facebook_browser.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,13 @@ def get_token(self):
105105

106106
return self.__cached_token
107107

108-
def query_graph_ql_birthday_comet_root(self, offset_month):
109-
""" Query the GraphQL BirthdayCometRootQuery endpoint that powers the https://www.facebook.com/events/birthdays page
108+
def query_graph_ql_birthday_comet_monthly(self, offset_month):
109+
""" Query the GraphQL BirthdayCometMonthlyBirthdaysRefetchQuery endpoint that powers the https://www.facebook.com/events/birthdays page
110110
This endpoint will return all Birthdays for the offset_month plus the following 2 consecutive months. """
111111

112112
FACEBOOK_GRAPHQL_ENDPOINT = 'https://www.facebook.com/api/graphql/'
113-
FACEBOOK_GRAPHQL_API_REQ_FRIENDLY_NAME = 'BirthdayCometRootQuery'
114-
DOC_ID = 3382519521824494
113+
FACEBOOK_GRAPHQL_API_REQ_FRIENDLY_NAME = 'BirthdayCometMonthlyBirthdaysRefetchQuery'
114+
DOC_ID = 5347559575302259
115115

116116
variables = {
117117
'offset_month': offset_month,

fb2cal/facebook_user.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1+
DATE_SEPERATOR = '/'
2+
UNKNOWN_CHAR = '?'
3+
14
class FacebookUser:
2-
def __init__(self, id, name, profile_url, profile_picture_uri, birthday_day, birthday_month):
5+
def __init__(self, id, name, profile_url, profile_picture_uri, birthday_day, birthday_month, birthday_year):
36
self.id = id
47
self.name = name
58
self.profile_url = profile_url
69
self.profile_picture_uri = profile_picture_uri
710
self.birthday_day = birthday_day
811
self.birthday_month = birthday_month
12+
self.birthday_year = birthday_year
913

1014
def __str__(self):
11-
return f'{self.name} ({self.birthday_day}/{self.birthday_month})'
12-
13-
def __unicode__(self):
14-
return u'{self.name} ({self.birthday_day}/{self.birthday_month})'
15+
day = f'{self.birthday_day:02}' if self.birthday_day else UNKNOWN_CHAR*2
16+
month = f'{self.birthday_month:02}' if self.birthday_month else UNKNOWN_CHAR*2
17+
year = f'{self.birthday_year:04}' if self.birthday_year else UNKNOWN_CHAR*4
18+
formatted_birthday = DATE_SEPERATOR.join(filter(None, (day, month, year)))
19+
return f'{self.name} ({formatted_birthday})'
1520

1621
def __lt__(self, other):
1722
return (self.birthday_month < other.birthday_month) and (self.birthday_day < other.birthday_month)
@@ -21,4 +26,3 @@ def __eq__(self, other):
2126

2227
def __hash__(self):
2328
return hash(self.id)
24-

fb2cal/ics_writer.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,36 @@ def generate(self):
2828
cur_date = datetime.now()
2929

3030
for facebook_user in self.facebook_users:
31-
e = Event()
32-
e.uid = facebook_user.id
33-
e.created = cur_date
34-
e.description = f'{facebook_user.name}\n{generate_facebook_profile_url_permalink(facebook_user)}'
35-
3631
# Don't add extra 's' if name already ends with 's'
3732
formatted_username = f"{facebook_user.name}'s" if facebook_user.name[-1] != 's' else f"{facebook_user.name}'"
38-
e.name = f'{formatted_username} Birthday'
33+
formatted_username = f'{formatted_username} Birthday'
3934

40-
# Calculate the year as this year or next year based on if its past current month or not
41-
# Also pad day, month with leading zeros to 2dp
42-
year = cur_date.year if facebook_user.birthday_month >= cur_date.month else (cur_date + relativedelta(years=1)).year
35+
# Set date components
36+
day = facebook_user.birthday_day
37+
month = facebook_user.birthday_month
38+
year = facebook_user.birthday_year
4339

4440
# Feb 29 special case:
4541
# If event year is not a leap year, use Feb 28 as birthday date instead
4642
if facebook_user.birthday_month == 2 and facebook_user.birthday_day == 29 and not calendar.isleap(year):
47-
facebook_user.birthday_day = 28
43+
day = 28
44+
45+
# The birth year may not be visible due to privacy settings
46+
# In this case, calculate the year as this year or next year based on if its past current month or not
47+
if year is None:
48+
year = cur_date.year if facebook_user.birthday_month >= cur_date.month else (cur_date + relativedelta(years=1)).year
4849

49-
month = '{:02d}'.format(facebook_user.birthday_month)
50-
day = '{:02d}'.format(facebook_user.birthday_day)
50+
# Format date components as needed
51+
month = f'{month:02}'
52+
day = f'{day:02}'
53+
54+
# Event meta data
55+
e = Event()
56+
57+
e.uid = facebook_user.id
58+
e.name = formatted_username
59+
e.created = cur_date
60+
e.description = f'{facebook_user}\n{generate_facebook_profile_url_permalink(facebook_user)}'
5161
e.begin = f'{year}-{month}-{day} 00:00:00'
5262
e.make_all_day()
5363
e.duration = timedelta(days=1)

fb2cal/transformer.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
class Transformer:
44

5-
def transform_birthday_comet_root_to_birthdays(self, birthday_comet_root_json):
6-
""" Transforms outfrom from BirthdayCometRootQuery to list of Birthdays """
5+
def transform_birthday_comet_monthly_to_birthdays(self, birthday_comet_root_json):
6+
""" Transforms outfrom from BirthdayCometMonthlyBirthdaysRefetchQuery to list of Birthdays """
77

88
facebook_users = []
99

1010
for all_friends_by_birthday_month_edge in birthday_comet_root_json['data']['viewer']['all_friends_by_birthday_month']['edges']:
1111
for friend_edge in all_friends_by_birthday_month_edge['node']['friends']['edges']:
1212
friend = friend_edge['node']
13-
13+
1414
# Create Birthday object
1515
facebook_users.append(
1616
FacebookUser(
@@ -19,7 +19,8 @@ def transform_birthday_comet_root_to_birthdays(self, birthday_comet_root_json):
1919
friend["profile_url"],
2020
friend["profile_picture"]["uri"],
2121
friend["birthdate"]["day"],
22-
friend["birthdate"]["month"]
22+
friend["birthdate"]["month"],
23+
friend["birthdate"]["year"]
2324
))
2425

2526
return facebook_users

tests/mocks/birthday_comet_root_mocks.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,7 +1956,8 @@
19561956
},
19571957
"birthdate":{
19581958
"day":1,
1959-
"month":11
1959+
"month":11,
1960+
"year":1982
19601961
},
19611962
"__module_operation_BirthdayCometMonthlyBirthdaysCard_allFriendsByBirthdayMonthEdge":{
19621963
"__dr":"BirthdayCometProfilePictureOnUser_user$normalization.graphql"
@@ -2019,7 +2020,8 @@
20192020
},
20202021
"birthdate":{
20212022
"day":25,
2022-
"month":12
2023+
"month":12,
2024+
"year":None
20232025
},
20242026
"__module_operation_BirthdayCometMonthlyBirthdaysCard_allFriendsByBirthdayMonthEdge":{
20252027
"__dr":"BirthdayCometProfilePictureOnUser_user$normalization.graphql"
@@ -2082,7 +2084,8 @@
20822084
},
20832085
"birthdate":{
20842086
"day":17,
2085-
"month":1
2087+
"month":1,
2088+
"year":1881
20862089
},
20872090
"__module_operation_BirthdayCometMonthlyBirthdaysCard_allFriendsByBirthdayMonthEdge":{
20882091
"__dr":"BirthdayCometProfilePictureOnUser_user$normalization.graphql"

tests/test_ics_writer.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ def setUp(self):
1414
'https://scontent-syd2-1.xx.fbcdn.net/v/t1.0-1/cp0/p60x60/00000001_10161077510019848_299841799451806933_o.jpg',
1515
20,
1616
1,
17+
1994
1718
),
1819
FacebookUser(
1920
'100000001',
@@ -22,6 +23,7 @@ def setUp(self):
2223
'https://scontent-syd2-1.xx.fbcdn.net/v/t1.0-1/cp0/p60x60/00000002_10161077510019848_299841799451806933_o.jpg',
2324
12,
2425
3,
26+
1974
2527
),
2628
FacebookUser(
2729
'100000002',
@@ -30,6 +32,7 @@ def setUp(self):
3032
'https://scontent-syd2-1.xx.fbcdn.net/v/t1.0-1/cp0/p60x60/00000002_10161077510019848_299841799451806933_o.jpg',
3133
6,
3234
6,
35+
2001
3336
),
3437
FacebookUser(
3538
'100000003',
@@ -38,6 +41,7 @@ def setUp(self):
3841
'https://scontent-syd2-1.xx.fbcdn.net/v/t1.0-1/cp0/p60x60/00000003_10161077510019848_299841799451806933_o.jpg',
3942
26,
4043
10,
44+
1987
4145
),
4246
FacebookUser(
4347
'100000004',
@@ -46,6 +50,7 @@ def setUp(self):
4650
'https://scontent-syd2-1.xx.fbcdn.net/v/t1.0-1/cp0/p60x60/00000004_10161077510019848_299841799451806933_o.jpg',
4751
29,
4852
2,
53+
2004
4954
),
5055
FacebookUser(
5156
'100000005',
@@ -54,6 +59,7 @@ def setUp(self):
5459
'https://scontent-syd2-1.xx.fbcdn.net/v/t1.0-1/cp0/p60x60/00000005_10161077510019848_299841799451806933_o.jpg',
5560
31,
5661
12,
62+
None
5763
),
5864
FacebookUser(
5965
'100000006',
@@ -62,6 +68,7 @@ def setUp(self):
6268
'https://scontent-syd2-1.xx.fbcdn.net/v/t1.0-1/cp0/p60x60/00000005_10161077510019848_299841799451806933_o.jpg',
6369
24,
6470
5,
71+
None
6572
),
6673
]
6774
self.ics_writer = ICSWriter(self.facebook_users)
@@ -78,45 +85,45 @@ def test_ics_writer_equivalence(self):
7885
CALSCALE:GREGORIAN
7986
BEGIN:VEVENT
8087
RRULE:FREQ=YEARLY
81-
DTSTART;VALUE=DATE:20210120
88+
DTSTART;VALUE=DATE:19940120
8289
DTSTAMP:20201113T071402Z
83-
DESCRIPTION:John Smith\\nhttps://www.facebook.com/100000000
90+
DESCRIPTION:John Smith (20/01/1994)\\nhttps://www.facebook.com/100000000
8491
DURATION:P1D
8592
SUMMARY:John Smith's Birthday
8693
UID:100000000
8794
END:VEVENT
8895
BEGIN:VEVENT
8996
RRULE:FREQ=YEARLY
90-
DTSTART;VALUE=DATE:20210312
97+
DTSTART;VALUE=DATE:19740312
9198
DTSTAMP:20201113T071402Z
92-
DESCRIPTION:Laura Daisy\\nhttps://www.facebook.com/100000001
99+
DESCRIPTION:Laura Daisy (12/03/1974)\\nhttps://www.facebook.com/100000001
93100
DURATION:P1D
94101
SUMMARY:Laura Daisy's Birthday
95102
UID:100000001
96103
END:VEVENT
97104
BEGIN:VEVENT
98105
RRULE:FREQ=YEARLY
99-
DTSTART;VALUE=DATE:20210606
106+
DTSTART;VALUE=DATE:20010606
100107
DTSTAMP:20201113T071402Z
101-
DESCRIPTION:韩忠清\\nhttps://www.facebook.com/100000002
108+
DESCRIPTION:韩忠清 (06/06/2001)\\nhttps://www.facebook.com/100000002
102109
DURATION:P1D
103110
SUMMARY:韩忠清's Birthday
104111
UID:100000002
105112
END:VEVENT
106113
BEGIN:VEVENT
107114
RRULE:FREQ=YEARLY
108-
DTSTART;VALUE=DATE:20211026
115+
DTSTART;VALUE=DATE:19871026
109116
DTSTAMP:20201113T071402Z
110-
DESCRIPTION:حكيم هديّة\\nhttps://www.facebook.com/100000003
117+
DESCRIPTION:حكيم هديّة (26/10/1987)\\nhttps://www.facebook.com/100000003
111118
DURATION:P1D
112119
SUMMARY:حكيم هديّة's Birthday
113120
UID:100000003
114121
END:VEVENT
115122
BEGIN:VEVENT
116123
RRULE:FREQ=YEARLY
117-
DTSTART;VALUE=DATE:20210228
124+
DTSTART;VALUE=DATE:20040229
118125
DTSTAMP:20201113T071402Z
119-
DESCRIPTION:Leap Year\\nhttps://www.facebook.com/100000004
126+
DESCRIPTION:Leap Year (29/02/2004)\\nhttps://www.facebook.com/100000004
120127
DURATION:P1D
121128
SUMMARY:Leap Year's Birthday
122129
UID:100000004
@@ -125,7 +132,7 @@ def test_ics_writer_equivalence(self):
125132
RRULE:FREQ=YEARLY
126133
DTSTART;VALUE=DATE:20201231
127134
DTSTAMP:20201113T071402Z
128-
DESCRIPTION:Mónica Bellucci\\nhttps://www.facebook.com/100000005
135+
DESCRIPTION:Mónica Bellucci (31/12/????)\\nhttps://www.facebook.com/100000005
129136
DURATION:P1D
130137
SUMMARY:Mónica Bellucci's Birthday
131138
UID:100000005
@@ -134,7 +141,7 @@ def test_ics_writer_equivalence(self):
134141
RRULE:FREQ=YEARLY
135142
DTSTART;VALUE=DATE:20210524
136143
DTSTAMP:20201113T071402Z
137-
DESCRIPTION:Bob Jones\\nhttps://www.facebook.com/100000006
144+
DESCRIPTION:Bob Jones (24/05/????)\\nhttps://www.facebook.com/100000006
138145
DURATION:P1D
139146
SUMMARY:Bob Jones' Birthday
140147
UID:100000006

tests/test_transformer.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
class TestTransformer(unittest.TestCase):
77
def setUp(self):
88
self.transformer = Transformer()
9-
self.facebook_users = self.transformer.transform_birthday_comet_root_to_birthdays(BIRTHDAY_COMET_ROOT_JANUARY_MOCK)
9+
self.facebook_users = self.transformer.transform_birthday_comet_monthly_to_birthdays(BIRTHDAY_COMET_ROOT_JANUARY_MOCK)
1010

1111
def test_count(self):
1212
self.assertEqual(len(self.facebook_users), 3)
@@ -19,6 +19,7 @@ def test_friend_in_november(self):
1919
self.assertEqual(friend.profile_picture_uri, 'https://scontent-syd2-1.xx.fbcdn.net/v/t1.0-1/cp0/p60x60/122897864_10161077510019848_299841799681806933_o.jpg?_nc_cat=107&ccb=2&_nc_sid=7206a8&_nc_ohc=yzAYhtdvoMYAX9Zxo1e&_nc_ht=scontent-syd2-1.xx&tp=27&oh=dc48247e31223151bc5d55781a572e2f&oe=5FD254D0')
2020
self.assertEqual(friend.birthday_day, 1)
2121
self.assertEqual(friend.birthday_month, 11)
22+
self.assertEqual(friend.birthday_year, 1982)
2223

2324
def test_friend_in_december(self):
2425
friend = self.facebook_users[1]
@@ -28,6 +29,7 @@ def test_friend_in_december(self):
2829
self.assertEqual(friend.profile_picture_uri, 'https://scontent-syd2-1.xx.fbcdn.net/v/t1.0-1/cp0/p60x60/53497864_10161077510019848_299841799451806933_o.jpg?_nc_cat=107&ccb=2&_nc_sid=7206a8&_nc_ohc=yzAYhtdvoMYAX9Zxo1e&_nc_ht=scontent-syd2-1.xx&tp=27&oh=dc48247e31223151bc5d55781a572e2f&oe=5FD254D0')
2930
self.assertEqual(friend.birthday_day, 25)
3031
self.assertEqual(friend.birthday_month, 12)
32+
self.assertEqual(friend.birthday_year, None)
3133

3234
def test_friend_in_january(self):
3335
friend = self.facebook_users[2]
@@ -37,3 +39,4 @@ def test_friend_in_january(self):
3739
self.assertEqual(friend.profile_picture_uri, 'https://scontent-syd2-1.xx.fbcdn.net/v/t1.0-1/cp0/p60x60/34f34864_10161077510019848_299841799681806933_o.jpg?_nc_cat=107&ccb=2&_nc_sid=7406a8&_nc_ohc=yzAYhtdvoMYAX9Zxo1e&_nc_ht=scontent-syd2-1.xx&tp=27&oh=dc48247e31223151bc5d55781a572e2f&oe=5FD254D0')
3840
self.assertEqual(friend.birthday_day, 17)
3941
self.assertEqual(friend.birthday_month, 1)
42+
self.assertEqual(friend.birthday_year, 1881)

0 commit comments

Comments
 (0)