diff --git a/_data/events.yml b/_data/events.yml index 5989e62..d4fd17e 100644 --- a/_data/events.yml +++ b/_data/events.yml @@ -1953,9 +1953,9 @@ path: https://www.meetup.com/women-coding-community/events/313256761/ title: View meetup event target: _target - - title: | Java bootcamp - Session 5 + uid: "event_313256771@meetup.com" description: | Do you want to build strong Java skills but don't know where to start? Do you feel overwhelmed juggling tutorials, documentation, and scattered resources while trying to learn Java on your own? Our Java Bootcamp is designed to give you a clear, structured path to build practical Java skills through hands-on coding, not endless theory. category_style: tech-talk @@ -1974,18 +1974,19 @@ target: _target - title: | - Java bootcamp - Session 6 + Java Bootcamp: Vibe Coding with AI Agents & MCP + uid: "event_313257077@meetup.com" description: | - Do you want to build strong Java skills but don't know where to start? Do you feel overwhelmed juggling tutorials, documentation, and scattered resources while trying to learn Java on your own? Our Java Bootcamp is designed to give you a clear, structured path to build practical Java skills through hands-on coding, not endless theory. + Ready to build a Java project even if you've never written a line of Java before? This special edition of our Java Bootcamp series brings together everything we've been building with you, and takes it one step further. category_style: tech-talk category_name: Tech Talk date: THU, MAR 12, 2026 expiration: "20260312" - host: "Zaynah Ahmed" - speaker: "Adriana Zencke Zimmermann, Sonali Goel" - time: 07:00 PM GMT + host: "" + speaker: "" + time: 06:30 PM GMT image: - path: "https://secure.meetupstatic.com/photos/event/2/4/4/9/600_532629289.jpeg" + path: "https://secure.meetupstatic.com/photos/event/5/4/a/2/600_532941666.jpeg" alt: WCC Meetup event image link: path: https://www.meetup.com/women-coding-community/events/313257077/ @@ -1993,26 +1994,28 @@ target: _target - title: | - March Book Club: TBD + Java Bootcamp: The Grand Finale Hack Hour + uid: "event_313574241@meetup.com" description: | - Women Coding Community Book Club! March 2026 Book: TBD If you would like to have a say in our next book club read, do join our channel progbookclub. - category_style: book-club - category_name: Book Club - date: MON, MAR 30, 2026 - expiration: "20260330" - host: "Silke Nodwell and Prabha Venkatesh" + The Women Coding Community (WCC) is wrapping up our Java Fullstack Bootcamp with a high-energy, hands-on Hack Hour session. + category_style: tech-talk + category_name: Tech Talk + date: FRI, MAR 13, 2026 + expiration: "20260313" + host: "" speaker: "" - time: 07:00 PM BST + time: 06:00 PM GMT image: - path: "https://secure.meetupstatic.com/photos/event/a/6/e/d/600_521202733.jpeg" + path: "https://secure.meetupstatic.com/photos/event/9/4/0/7/600_532957895.jpeg" alt: WCC Meetup event image link: - path: https://www.meetup.com/women-coding-community/events/313316083/ + path: https://www.meetup.com/women-coding-community/events/313574241/ title: View meetup event target: _target - title: | Own your voice: Assertive Communication for Women in Tech + uid: "event_313463081@meetup.com" description: "We\u2019re thrilled to re-launch the Women Coding Community Career Club! We are kicking off with a powerful session led by Yasuko Ohtake, a leading technologist who will share candid insights from her career and practical strategies for visibility and influence in tech.\n" category_style: tech-talk category_name: Tech Talk @@ -2029,27 +2032,9 @@ title: View meetup event target: _target -- title: | - March Book Club: AI Engineering pt.2 and Showcase - description: | - Women Coding Community Book Club! March 2026 Book: AI Engineering by Chip Huyen (continued) With a small showcase of AI engineering projects by WCC members If you would like to have a say in our next book club read, do join our channel progbookclub. - category_style: book-club - category_name: Book Club - date: MON, MAR 30, 2026 - expiration: "20260330" - host: "Silke Nodwell, Prabha Venkatesh" - speaker: "" - time: 07:00 PM BST - image: - path: "https://secure.meetupstatic.com/photos/event/7/0/d/7/600_532888887.jpeg" - alt: WCC Meetup event image - link: - path: https://www.meetup.com/women-coding-community/events/313316083/ - title: View meetup event - target: _target - - title: | 2026 International Women's Day Celebration - Feast, Talks & Tech Community + uid: "event_313538360@meetup.com" description: | In partnership with Nando's Women in Engineering Celebrate IWD with the best women in tech and incredible food! This International Women's Day, a number of London's most powerful women-in-tech communities are coming together for an unforgettable evening of inspiring talks, a delicious feast, a candid panel discussion, and genuine connection. category_style: tech-talk @@ -2068,39 +2053,41 @@ target: _target - title: | - Java Bootcamp: Vibe Coding with AI Agents & MCP + March Book Club: AI Engineering pt.2 and Showcase + uid: "event_313316083@meetup.com" description: | - Ready to build a Java project even if you've never written a line of Java before? This special edition of our Java Bootcamp series brings together everything we've been building with you, and takes it one step further. - category_style: tech-talk - category_name: Tech Talk - date: THU, MAR 12, 2026 - expiration: "20260312" - host: "" + Women Coding Community Book Club! March 2026 Book: AI Engineering by Chip Huyen (continued) With a small showcase of AI engineering projects by WCC members If you would like to have a say in our next book club read, do join our channel progbookclub. + category_style: book-club + category_name: Book Club + date: MON, MAR 30, 2026 + expiration: "20260330" + host: "Silke Nodwell, Prabha Venkatesh" speaker: "" - time: 06:30 PM GMT + time: 07:00 PM BST image: - path: "https://secure.meetupstatic.com/photos/event/5/4/a/2/600_532941666.jpeg" + path: "https://secure.meetupstatic.com/photos/event/7/0/d/7/600_532888887.jpeg" alt: WCC Meetup event image link: - path: https://www.meetup.com/women-coding-community/events/313257077/ + path: https://www.meetup.com/women-coding-community/events/313316083/ title: View meetup event target: _target - title: | - Java Bootcamp: The Grand Finale Hack Hour + From Idea to Impact: Building Products That Actually Matter + uid: "event_313608754@meetup.com" description: | - The Women Coding Community (WCC) is wrapping up our Java Fullstack Bootcamp with a high-energy, hands-on Hack Hour session. + From Idea to Impact: Building Products That Actually Matter How to define real user problems, prioritize effectively, and lead cross-functional teams to deliver meaningful results. category_style: tech-talk category_name: Tech Talk - date: FRI, MAR 13, 2026 - expiration: "20260313" + date: WED, APR 01, 2026 + expiration: "20260401" host: "" speaker: "" - time: 06:00 PM GMT + time: 07:00 PM BST image: - path: "https://secure.meetupstatic.com/photos/event/9/4/0/7/600_532957895.jpeg" + path: "https://secure.meetupstatic.com/photos/event/7/4/3/1/600_533009745.jpeg" alt: WCC Meetup event image link: - path: https://www.meetup.com/women-coding-community/events/313574241/ + path: https://www.meetup.com/women-coding-community/events/313608754/ title: View meetup event target: _target diff --git a/tools/meetup_import.py b/tools/meetup_import.py index 50e2450..55fe824 100644 --- a/tools/meetup_import.py +++ b/tools/meetup_import.py @@ -60,6 +60,7 @@ class WebLink(BaseModel): class MeetupEvents(BaseModel): title: str + uid: str description: str category_style: Optional[str] = "tech-talk" category_name: Optional[str] = "Tech Talk" @@ -191,6 +192,7 @@ def get_upcoming_meetups_from_ical_file(ical_path: str) -> list[MeetupEvents]: date = date_obj.strftime("%a, %b %d, %Y").upper() time = event.begin.datetime.strftime("%I:%M %p %Z") url = event.url or "" + uid = event.uid full_description = (event.description or "").strip() @@ -230,6 +232,7 @@ def get_upcoming_meetups_from_ical_file(ical_path: str) -> list[MeetupEvents]: speaker=speaker, image=Image(path=image_url, alt="WCC Meetup event image"), link=WebLink(path=url), + uid=uid, ) ) return upcoming_meetups @@ -241,6 +244,7 @@ def process_meetup_data(meetup: dict) -> dict: meetup["expiration"] = QuotedString(meetup["expiration"]) meetup["host"] = QuotedString(meetup.get("host", "")) meetup["speaker"] = QuotedString(meetup.get("speaker", "")) + meetup["uid"] = to_quoted_str(meetup.get("uid", "")) if "image" in meetup: meetup["image"]["path"] = to_quoted_str(meetup["image"]["path"]) meetup["image"]["alt"] = to_quoted_str(meetup["image"]["alt"]) @@ -248,14 +252,6 @@ def process_meetup_data(meetup: dict) -> dict: meetup["link"]["title"] = to_quoted_str(meetup["link"]["title"]) return meetup -# --- Create a unique key for an event using "title - date" ---- -def get_event_key(event): - return f"{event.get('title').strip()} - {event.get('date')}" - -# --- Get a Set of keys for existing events ---- -def get_existing_event_keys(events): - return {get_event_key(e) for e in events} - # --- Get existing events in yml file ---- def load_existing_events_from_file(file_path): try: @@ -278,6 +274,21 @@ def append_events_to_yaml_file(file_path, data): logging.error(f"Error appending new events to file '{file_path}': {e}") raise +def get_event_key(event: dict) -> str: + return event.get("uid", "") + +def get_existing_event_keys(existing_events: list[dict]) -> set: + return {get_event_key(event) for event in existing_events} + +def get_added_events(upcoming_events: list[dict], existing_events: list[dict]) -> list[dict]: + existing_keys = get_existing_event_keys(existing_events) + added_events = [] + for event in upcoming_events: + event_key = get_event_key(event) + if event_key not in existing_keys: + added_events.append(event) + return added_events + # --- Script Start --- def fetch_events(): logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') @@ -289,7 +300,7 @@ def fetch_events(): upcoming_events = get_upcoming_meetups_from_ical_file(ical_file_path) existing_events = load_existing_events_from_file(yml_file_path) - existing_keys = get_existing_event_keys(existing_events) + existing_keys = {event.get("uid") for event in existing_events} added_events = [] logging.info("Upcoming Meetup Events:") @@ -297,7 +308,7 @@ def fetch_events(): logging.info(f"{event.title}") formatted_event = process_meetup_data(event.model_dump()) - event_key = get_event_key(formatted_event) + event_key = formatted_event["uid"] if event_key not in existing_keys: added_events.append(formatted_event) diff --git a/tools/tests/meetup_import_test.py b/tools/tests/meetup_import_test.py index 32696b8..4a141ea 100644 --- a/tools/tests/meetup_import_test.py +++ b/tools/tests/meetup_import_test.py @@ -12,11 +12,12 @@ get_event_image_url, to_literal_str, to_quoted_str, + get_event_key, + get_existing_event_keys, + get_added_events, LiteralString, QuotedString, NoQuoteString, - get_event_key, - get_existing_event_keys, load_existing_events_from_file, process_meetup_data ) @@ -88,14 +89,37 @@ def test_to_literal_and_quoted_str(): assert isinstance(to_quoted_str('Hello!'), QuotedString) assert isinstance(to_quoted_str('Simple'), NoQuoteString) -def test_get_event_key(): - event = {'title': ' Talk ', 'date': 'JAN 1, 2025'} - assert get_event_key(event) == 'Talk - JAN 1, 2025' +def test_no_new_events_are_added_if_all_events_exist(): + existing_events = [{'title': 'Talk', 'date': 'JAN 1, 2025', 'uid': 'talk-jan-1-2025'}] + existing_keys = get_existing_event_keys(existing_events) + new_event = {'title': 'Talk (date updated)', 'date': 'JAN 2, 2025', 'uid': 'talk-jan-1-2025'} + new_key = get_event_key(new_event) + assert new_key in existing_keys + +def test_get_added_events_only_new(): + existing = [{'uid': 'event-1', 'title': 'Talk'}] + upcoming = [ + {'uid': 'event-1', 'title': 'Talk'}, + {'uid': 'event-2', 'title': 'Workshop'}, + {'uid': 'event-3', 'title': 'Panel'} + ] + added = get_added_events(upcoming, existing) + assert len(added) == 2 + assert added[0]['uid'] == 'event-2' + assert added[1]['uid'] == 'event-3' + +def test_get_added_events_with_empty_existing(): + existing = [] + upcoming = [{'uid': 'event-1', 'title': 'Talk'}] + added = get_added_events(upcoming, existing) + assert len(added) == 1 + assert added[0]['uid'] == 'event-1' -def test_get_existing_event_keys(): - events = [{'title': 'A', 'date': '1'}, {'title': 'B', 'date': '2'}] - keys = get_existing_event_keys(events) - assert len(keys) == 2 and all(isinstance(k, str) for k in keys) +def test_get_added_events_with_empty_upcoming(): + existing = [{'uid': 'event-1', 'title': 'Talk'}] + upcoming = [] + added = get_added_events(upcoming, existing) + assert len(added) == 0 def test_load_existing_events_from_file(tmp_path): events = [{'title': 'E1', 'date': 'D1'}]