99
1010logger = logging .getLogger (__name__ )
1111
12+ _EVENT_LISTING_QUERY = """\
13+ query($first: Int = 20, $urlName: String!) {
14+ groupByUrlname(urlname: $urlName) {
15+ events(first: $first, sort: ASC) {
16+ pageInfo {
17+ hasNextPage
18+ endCursor
19+ }
20+ edges {
21+ node {
22+ id
23+ group {
24+ name
25+ city
26+ state
27+ country
28+ }
29+ title
30+ dateTime
31+ eventUrl
32+ venues {
33+ lat
34+ lon
35+ city
36+ state
37+ country
38+ venueType
39+ }
40+ }
41+ }
42+ }
43+ }
44+ }
45+ """
1246
1347class TwirMeetupClient :
1448 AUTH_ENDPOINT = "https://secure.meetup.com/oauth2/access"
15- GQL_ENDPOINT = "https://api.meetup.com/gql"
49+ GQL_ENDPOINT = "https://api.meetup.com/gql-ext "
1650
1751 def __init__ (self ) -> None :
1852 self ._access_token = None
@@ -52,154 +86,22 @@ def _get_access_token(self):
5286 return self ._access_token
5387
5488 def _build_event_listing_gql_query (self , group_url_name : str ) -> dict :
55- return {
56- "query" : """
57- query ($urlName: String!, $searchEventInput: ConnectionInput!) {
58- groupByUrlname(urlname: $urlName) {
59- upcomingEvents(input: $searchEventInput, sortOrder: ASC) {
60- pageInfo {
61- hasNextPage
62- endCursor
63- }
64- edges {
65- node {
66- id
67- group {
68- name
69- city
70- state
71- country
72- }
73- title
74- dateTime
75- eventUrl
76- venue {
77- city
78- state
79- country
80- venueType
81- lat
82- lng
83- }
84- }
85- }
86- }
87- }
88- }
89- """ ,
90- "variables" : {
91- "urlName" : group_url_name ,
92- "searchEventInput" : {
93- # TODO: see if we need this limit or not
94- "first" : 20
95- }
96- }
97- }
89+ return { "query" : _EVENT_LISTING_QUERY , "variables" : { "urlName" : group_url_name } }
9890
9991 def _parse_event_listing_gql_response (self , response : dict ) -> List [RawGqlEvent ]:
100- edges = response ["groupByUrlname" ]["upcomingEvents " ]["edges" ]
92+ edges = response ["groupByUrlname" ]["events " ]["edges" ]
10193
10294 events = []
10395 # TODO: maybe move this validation somewhere else?
10496 for edge_kwargs in edges :
105- if not edge_kwargs ["node" ]["venue " ]:
106- logger .error (f"Event response missing venue : { edge_kwargs } " )
97+ if not edge_kwargs ["node" ]["venues " ]:
98+ logger .error (f"Event response missing venues : { edge_kwargs } " )
10799 continue
108100
109101 events .append (RawGqlEvent (** edge_kwargs ))
110102
111103 return events
112104
113- def fetch_groups (self , endCursor = "" ):
114- """
115- Returns the response from the API call, which includes data on groups matching the criteria specified in the GraphQL query.
116- :type endCursor: An optional string parameter used for pagination, indicating the starting point of the query for fetching subsequent pages of results
117- :rtype: requests.Response
118- """
119-
120- # Sets the content type to application/json for the request body.
121- headers = {
122- "Authorization" : f"Bearer { self ._get_access_token ()} " ,
123- "Content-Type" : "application/json" ,
124- }
125-
126- # GraphQL Query:
127- # Below is a GraphQL query that requests information about groups such as ID, name, link, URL name, latitude, and longitude.
128- data = {
129- "query" : """
130- query (
131- $searchGroupInput: ConnectionInput!,
132- $searchGroupFilter: SearchConnectionFilter!,
133- $sortOrder: KeywordSort!
134- ) {
135- keywordSearch(
136- input: $searchGroupInput,
137- filter: $searchGroupFilter,
138- sort: $sortOrder
139- ) {
140- pageInfo {
141- hasNextPage
142- endCursor
143- }
144- edges {
145- node {
146- result {
147- ... on Group {
148- id
149- name
150- link
151- urlname
152- latitude
153- longitude
154- }
155- }
156- }
157- }
158- }
159- }
160- """ ,
161- # The query filters results based on the keyword "Rust" and sorts them by relevance
162- "variables" : {
163- "searchGroupFilter" : {
164- "query" : "Rust" ,
165- "lat" : 0.0 ,
166- "lon" : 0.0 ,
167- "radius" : 20000 ,
168- "source" : "GROUPS"
169- },
170- "searchGroupInput" : {
171- "first" : 200 ,
172- "after" : endCursor
173- },
174- "sortOrder" :{
175- "sortField" : "RELEVANCE"
176- }
177- }
178- }
179- return requests .post (url = self .GQL_ENDPOINT , headers = headers , json = data )
180-
181- def get_rust_groups (self ) -> dict :
182- """
183- Returns a dictionary where each key represents the unique ID of a group, and the corresponding value is another dictionary containing details about the group such as name, link, URL name, latitude, and longitude
184- :rtype: dict
185- """
186- endCursor = None
187- groups = dict ()
188- while True :
189- response = self .fetch_groups (endCursor ).json ()
190- data = response ['data' ]
191- edges = data ['keywordSearch' ]['edges' ]
192- pageInfo = data ['keywordSearch' ]['pageInfo' ]
193- for node in edges :
194- group = node ["node" ]["result" ]
195- if not (group ["id" ] in groups ):
196- groups [group ["id" ]] = group
197- if pageInfo ['hasNextPage' ]:
198- endCursor = pageInfo ['endCursor' ]
199- else :
200- break
201- return groups
202-
203105 def get_raw_events_for_group (self , group : MeetupGroupUrl ) -> List [RawGqlEvent ]:
204106 headers = {
205107 "Authorization" : f"Bearer { self ._get_access_token ()} " ,
@@ -209,6 +111,7 @@ def get_raw_events_for_group(self, group: MeetupGroupUrl) -> List[RawGqlEvent]:
209111 logger .info (f"Fetching events for group { group } " )
210112 query = self ._build_event_listing_gql_query (group .url_name )
211113 response = requests .post (url = self .GQL_ENDPOINT , headers = headers , json = query )
114+ response .raise_for_status ()
212115 data = response .json ()["data" ]
213116 logger .debug (data )
214117
@@ -217,4 +120,3 @@ def get_raw_events_for_group(self, group: MeetupGroupUrl) -> List[RawGqlEvent]:
217120 return []
218121
219122 return self ._parse_event_listing_gql_response (data )
220-
0 commit comments