diff --git a/pythonsd/tests/__init__.py b/pythonsd/tests/__init__.py index 5056640..6e45a53 100644 --- a/pythonsd/tests/__init__.py +++ b/pythonsd/tests/__init__.py @@ -95,21 +95,24 @@ def setUp(self): self.expected_events = [ { - "link": "https://www.meetup.com/pythonsd/events/fdzbnqyznbqb/", - "name": "Saturday Study Group", - "datetime": "2019-10-12T12:00:00-07:00", - "venue": "UCSD Geisel Library", + "id": "305930677", + "link": "https://www.meetup.com/pythonsd/events/305930677/", + "name": "SDPy Monthly Meetup", + "datetime": "2025-05-22T19:00:00-07:00", + "venue": "Qualcomm Building Q", }, { - "link": "https://www.meetup.com/pythonsd/events/fdzbnqyznbzb/", - "name": "Saturday Study Group", - "datetime": "2019-10-19T12:00:00-07:00", - "venue": "UCSD Geisel Library", + "id": "305930676", + "link": "https://www.meetup.com/pythonsd/events/305930676/", + "name": "SDPy Monthly Meetup", + "datetime": "2025-06-26T19:00:00-07:00", + "venue": "Qualcomm Building Q", }, { - "link": "https://www.meetup.com/pythonsd/events/zgtnxqyznbgc/", - "name": "Monthly Meetup", - "datetime": "2019-10-24T19:00:00-07:00", + "id": "305930675", + "link": "https://www.meetup.com/pythonsd/events/305930675/", + "name": "SDPy Monthly Meetup", + "datetime": "2025-07-24T19:00:00-07:00", "venue": "Qualcomm Building Q", }, ] @@ -131,28 +134,28 @@ def test_preloaded_events(self): return_value=self.expected_events, ) as mock_get: response = self.client.get(self.url) - self.assertContains(response, "UCSD Geisel Library") self.assertContains(response, "Qualcomm Building Q") + self.assertContains(response, "SDPy Monthly Meetup") @responses.activate def test_html_widget(self): responses.add( - responses.GET, + responses.POST, UpcomingEventsView.MEETUP_EVENT_API_URL, json=self.api_response, status=200, ) response = self.client.get(self.url) - self.assertContains(response, "UCSD Geisel Library") self.assertContains(response, "Qualcomm Building Q") + self.assertContains(response, "SDPy Monthly Meetup") @responses.activate def test_api_noevents(self): responses.add( - responses.GET, + responses.POST, UpcomingEventsView.MEETUP_EVENT_API_URL, - json=[], + json={"data": {"groupByUrlname": {"events": {"edges": []}}}}, status=400, ) @@ -160,9 +163,9 @@ def test_api_noevents(self): self.assertContains(response, "There are no upcoming events") @responses.activate - def test_api_error(self): + def test_api_exception(self): responses.add( - responses.GET, + responses.POST, UpcomingEventsView.MEETUP_EVENT_API_URL, body=Exception("Error connecting..."), ) @@ -170,6 +173,17 @@ def test_api_error(self): response = self.client.get(self.url) self.assertContains(response, "There are no upcoming events") + @responses.activate + def test_api_error(self): + responses.add( + responses.POST, + UpcomingEventsView.MEETUP_EVENT_API_URL, + json={"errors": ["There was some error querying meetup.com"]}, + ) + + response = self.client.get(self.url) + self.assertContains(response, "There are no upcoming events") + class TestYouTubeRecentVideosView(test.TestCase): def setUp(self): diff --git a/pythonsd/tests/data/meetup-events-api.json b/pythonsd/tests/data/meetup-events-api.json index 471cae6..d8b03b0 100644 --- a/pythonsd/tests/data/meetup-events-api.json +++ b/pythonsd/tests/data/meetup-events-api.json @@ -1,146 +1,49 @@ -[ - { - "status": "upcoming", - "local_date": "2019-10-12", - "how_to_find_us": "Parking is free on the weekends in the Hopkins parking structure. We will usually be near Audrey's Cafe. Turn right after entering the library and look near the cafe first, or check the comments below for the table location.", - "group": { - "who": "Python Developers", - "name": "San Diego Python Users Group", - "join_mode": "open", - "country": "us", - "region": "en_US", - "created": 1326433663000, - "lon": -117.19999694824219, - "state": "CA", - "localized_location": "San Diego, CA", - "timezone": "US/Pacific", - "lat": 32.7599983215332, - "urlname": "pythonsd", - "id": 3096872 - }, - "name": "Saturday Study Group", - "created": 1544308522000, - "member_pay_fee": false, - "venue": { - "city": "San Diego", - "name": "UCSD Geisel Library", - "zip": "", - "repinned": true, - "lon": -117.23896026611328, - "localized_country_name": "USA", - "state": "CA", - "address_1": "Hopkins Parking Structure", - "country": "us", - "lat": 32.883792877197266, - "id": 24990930 - }, - "updated": 1544308522000, - "visibility": "public", - "date_in_series_pattern": false, - "yes_rsvp_count": 7, - "utc_offset": -25200000, - "local_time": "12:00", - "time": 1570906800000, - "duration": 10800000, - "waitlist_count": 0, - "id": "fdzbnqyznbqb", - "link": "https://www.meetup.com/pythonsd/events/fdzbnqyznbqb/", - "description": "
Every Saturday we meet up with the larger Python community (including San Diego PyLadies (http://www.meetup.com/sd-pyladies), Open BioInformatics San Diego (https://www.meetup.com/Open-Bioinformatics-SanDiego/) and San Diego Artificial Intelligence (https://www.meetup.com/SanDiegoAI/)) to chat, learn, and hang out. Our study group is for everyone on their Python journey, whether you're just starting out, an experienced pro, or anywhere in between.
There's no formal instruction, just a bunch of folks who like to code. (If you're new to Python, check out these handy resources (http://pythonsd.org/pages/getting-started.html)!)
We have a great time, come join us! Bring your laptop and any questions you have about Python. Free feel to show up at any time and stay as long as you want. Hope to see you there!
Python Community Code of Conduct (http://www.pythonsd.org/pages/code-of-conduct.html): Python is much more than a software language. Python is a vibrant community made up of members from around the globe with a diverse set of skills, personalities, and experiences. It is through these differences that our community experiences great successes and continued growth. Overall, we're good to each other. We contribute to this community not because we have to, but because we want to.
" - }, - { - "status": "upcoming", - "local_date": "2019-10-19", - "how_to_find_us": "Parking is free on the weekends in the Hopkins parking structure. We will usually be near Audrey's Cafe. Turn right after entering the library and look near the cafe first, or check the comments below for the table location.", - "group": { - "who": "Python Developers", - "name": "San Diego Python Users Group", - "join_mode": "open", - "country": "us", - "region": "en_US", - "created": 1326433663000, - "lon": -117.19999694824219, - "state": "CA", - "localized_location": "San Diego, CA", - "timezone": "US/Pacific", - "lat": 32.7599983215332, - "urlname": "pythonsd", - "id": 3096872 - }, - "name": "Saturday Study Group", - "created": 1544308522000, - "member_pay_fee": false, - "venue": { - "city": "San Diego", - "name": "UCSD Geisel Library", - "zip": "", - "repinned": true, - "lon": -117.23896026611328, - "localized_country_name": "USA", - "state": "CA", - "address_1": "Hopkins Parking Structure", - "country": "us", - "lat": 32.883792877197266, - "id": 24990930 - }, - "updated": 1544308522000, - "visibility": "public", - "date_in_series_pattern": false, - "yes_rsvp_count": 7, - "utc_offset": -25200000, - "local_time": "12:00", - "time": 1571511600000, - "duration": 10800000, - "waitlist_count": 0, - "id": "fdzbnqyznbzb", - "link": "https://www.meetup.com/pythonsd/events/fdzbnqyznbzb/", - "description": "Every Saturday we meet up with the larger Python community (including San Diego PyLadies (http://www.meetup.com/sd-pyladies), Open BioInformatics San Diego (https://www.meetup.com/Open-Bioinformatics-SanDiego/) and San Diego Artificial Intelligence (https://www.meetup.com/SanDiegoAI/)) to chat, learn, and hang out. Our study group is for everyone on their Python journey, whether you're just starting out, an experienced pro, or anywhere in between.
There's no formal instruction, just a bunch of folks who like to code. (If you're new to Python, check out these handy resources (http://pythonsd.org/pages/getting-started.html)!)
We have a great time, come join us! Bring your laptop and any questions you have about Python. Free feel to show up at any time and stay as long as you want. Hope to see you there!
Python Community Code of Conduct (http://www.pythonsd.org/pages/code-of-conduct.html): Python is much more than a software language. Python is a vibrant community made up of members from around the globe with a diverse set of skills, personalities, and experiences. It is through these differences that our community experiences great successes and continued growth. Overall, we're good to each other. We contribute to this community not because we have to, but because we want to.
" - }, - { - "status": "upcoming", - "local_date": "2019-10-24", - "how_to_find_us": "We are in the auditorium which is straight back through the cafeteria on the first floor. The doors are locked at 7:15 but there should be a sign posted with number to call to be let in.", - "group": { - "who": "Python Developers", - "name": "San Diego Python Users Group", - "join_mode": "open", - "country": "us", - "region": "en_US", - "created": 1326433663000, - "lon": -117.19999694824219, - "state": "CA", - "localized_location": "San Diego, CA", - "timezone": "US/Pacific", - "lat": 32.7599983215332, - "urlname": "pythonsd", - "id": 3096872 - }, - "name": "Monthly Meetup", - "created": 1556902700000, - "member_pay_fee": false, - "venue": { - "city": "San Diego", - "name": "Qualcomm Building Q", - "zip": "92121", - "repinned": false, - "lon": -117.20123291015625, - "localized_country_name": "USA", - "state": "CA", - "address_1": "6455 Lusk Blvd", - "country": "us", - "lat": 32.90226745605469, - "id": 26335029 - }, - "updated": 1556902700000, - "visibility": "public", - "date_in_series_pattern": false, - "yes_rsvp_count": 10, - "utc_offset": -25200000, - "local_time": "19:00", - "time": 1571968800000, - "duration": 7200000, - "waitlist_count": 0, - "id": "zgtnxqyznbgc", - "link": "https://www.meetup.com/pythonsd/events/zgtnxqyznbgc/", - "description": "Thanks to The Qualcomm Learning Center for hosting! This meetup will be in Qualcomm's Building Q auditorium. Cloudflare's Developer Relations will provide pizza!
AGENDA
We will be doing 5-7 minute lightning talks If you are interested in giving a talk, please post in the comments or get in touch with Diane (https://www.meetup.com/pythonsd/members/9220987/).
Currently Scheduled talks:
ANNOUNCEMENTS
We want your feedback improving San Diego Python! Post it here (https://goo.gl/forms/NmfGY4ReTTPms9cw1).
San Diego Python has a code of conduct (http://www.pythonsd.org/pages/code-of-conduct.html).
" +{ + "data": { + "groupByUrlname": { + "events": { + "edges": [ + { + "node": { + "id": "305930677", + "title": "SDPy Monthly Meetup", + "eventUrl": "https://www.meetup.com/pythonsd/events/305930677/", + "venues": [ + { + "name": "Qualcomm Building Q" + } + ], + "dateTime": "2025-05-22T19:00:00-07:00" + } + }, + { + "node": { + "id": "305930676", + "title": "SDPy Monthly Meetup", + "eventUrl": "https://www.meetup.com/pythonsd/events/305930676/", + "venues": [ + { + "name": "Qualcomm Building Q" + } + ], + "dateTime": "2025-06-26T19:00:00-07:00" + } + }, + { + "node": { + "id": "305930675", + "title": "SDPy Monthly Meetup", + "eventUrl": "https://www.meetup.com/pythonsd/events/305930675/", + "venues": [ + { + "name": "Qualcomm Building Q" + } + ], + "dateTime": "2025-07-24T19:00:00-07:00" + } + } + ] + } + } } -] +} diff --git a/pythonsd/views.py b/pythonsd/views.py index 42f636d..633c480 100644 --- a/pythonsd/views.py +++ b/pythonsd/views.py @@ -38,7 +38,11 @@ def get_context_data(self, **kwargs): class UpcomingEventsView(TemplateView): """Get upcoming events from Meetup.""" - MEETUP_EVENT_API_URL = "https://api.meetup.com/pythonsd/events" + # https://www.meetup.com/api/guide/ + MEETUP_EVENT_API_URL = "https://api.meetup.com/gql-ext" + + # https://www.meetup.com/pythonsd/ + MEETUP_GROUP_SLUG = "pythonsd" template_name = "pythonsd/fragments/upcoming-events.html" @@ -51,11 +55,33 @@ def get_upcoming_events(self): """Get upcoming events from Meetup.""" log.debug("Requesting upcoming events from Meetup.com") - # https://www.meetup.com/meetup_api/docs/:urlname/events/ + # Fetch the next 3 events from the API + # https://www.meetup.com/api/schema/#Group + # https://www.meetup.com/api/guide/#p02-querying-section + body = """ + query($urlname: String!) { + groupByUrlname(urlname: $urlname) { + events(first: 3) { + edges { + node { + id + title + venues { + name + } + eventUrl + dateTime + } + } + } + } + } + """ + try: - resp = requests.get( - self.MEETUP_EVENT_API_URL, - params={"photo-host": "public", "page": "3"}, + resp = requests.post( + url=self.MEETUP_EVENT_API_URL, + json={"query": body, "variables": {"urlname": self.MEETUP_GROUP_SLUG}}, timeout=5, ) except Exception: @@ -63,23 +89,37 @@ def get_upcoming_events(self): return [] if resp.ok: + data = resp.json() + + if "errors" in data: + log.error("GraphQL error fetching Meetup event feed: %s", data) + return [] + # Transform from meetup's API format into our format - events = [ - { - "link": e["link"], - "name": e["name"], - # Always show time in local San Diego time - "datetime": datetime.fromtimestamp( - e["time"] // 1000, - tz=zoneinfo.ZoneInfo(key=settings.TIME_ZONE), - ), - "venue": e["venue"]["name"] if "venue" in e else None, - } - for e in resp.json() - ] + events = [] + for edge in resp.json()["data"]["groupByUrlname"]["events"]["edges"]: + event = edge["node"] + events.append( + { + "id": event["id"], + "name": event["title"], + "link": event["eventUrl"], + # Meetup's API seems to return in our timezone + # but we'll be explicit here for future-proofing + "datetime": datetime.fromisoformat( + event["dateTime"] + ).astimezone(zoneinfo.ZoneInfo(key=settings.TIME_ZONE)), + # Technically an event can have multiple or no venue (it's an array) + "venue": ( + event["venues"][0]["name"] if event["venues"] else None + ), + } + ) return events else: - log.error("Error fetching Meetup event feed") + log.error( + "Error fetching Meetup event feed (status code=%s)", resp.status_code + ) return []