Skip to content

Commit 09f9ab7

Browse files
shireenraoabout-bot
and
about-bot
authored
Add all past events organized by Boston Python (#39)
* Add Past events * Reorg structure for past events * Pull latest data from meetup.com for past events * Add python script to update past-events page with past events from meetup.com * Create manual github action to update past-events * Delete 20220209-session-6-python-case-studies.md This is to test github actions to regenerate this last event * add print for events added and slightly change github action * fix nav-links image to not be relative * Comment and run action on schedule * Add manual trigger and on schedule trigger for action * use double quotes for cron * Update by setting trigger to UTC 1AM Tuesday * update to run 1:16 UTC * Update Past Events page * set schedule for 2:05AM Sunday Morning Co-authored-by: about-bot <[email protected]>
1 parent 1715ae5 commit 09f9ab7

File tree

338 files changed

+10109
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

338 files changed

+10109
-1
lines changed

.github/workflows/past-events.yml

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# This is a workflow which executes add_meetup_events.py every week to scrape the meeteup
2+
# page: https://www.meetup.com/bostonpython/events/past/ to pull past events
3+
# and create it's own event page under past-events and linked to a past-events/index.md
4+
5+
name: Update past-events page
6+
7+
# Controls when the workflow will run
8+
on:
9+
# Run on a schedule
10+
schedule:
11+
# Run at 2:05am early Sunday morning Eastern time (6/7:05 UTC)
12+
# https://crontab.guru/#5_7_*_*_0
13+
- cron: "5 7 * * 0"
14+
15+
# Allows you to run this workflow manually from the Actions tab
16+
workflow_dispatch:
17+
18+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
19+
jobs:
20+
# This workflow contains a single job called "build"
21+
build:
22+
# The type of runner that the job will run on
23+
runs-on: ubuntu-latest
24+
25+
# Steps represent a sequence of tasks that will be executed as part of the job
26+
steps:
27+
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
28+
- uses: actions/checkout@v2
29+
30+
- name: Set up Python
31+
uses: actions/setup-python@v1
32+
with:
33+
python-version: 3.8
34+
35+
- uses: actions/cache@v1
36+
name: Configure pip caching
37+
with:
38+
path: ~/.cache/pip
39+
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
40+
restore-keys: |
41+
${{ runner.os }}-pip-
42+
43+
- name: Install Python dependencies
44+
run: |
45+
python -m pip install --upgrade pip wheel
46+
python -m pip install -r requirements.txt
47+
48+
- name: Update past-events index
49+
run: |-
50+
ls -l past-events/index.md
51+
python add_meetup_events.py --rewrite
52+
ls -l past-events/index.md
53+
54+
- name: Commit and push if past-events/index.md changed
55+
run: |-
56+
git diff
57+
git config --global user.email "[email protected]"
58+
git config --global user.name "about-bot"
59+
git diff --quiet || (git add -A && git commit -m "Update Past Events page")
60+
git push

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
_site/
22
.DS_Store
3+
.venv/

_includes/custom-nav-links.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<a href='/'><img style="margin-top: 1em" src='assets/images/python-boston-sq.png' width='120' height='120'/></a>
1+
<a href='/'><img style="margin-top: 1em" src='/about/assets/images/python-boston-sq.png' width='120' height='120'/></a>

add_meetup_events.py

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"""
2+
Install the requirements in requirements.txt in a virtualenv
3+
4+
To see the latest output for past-events/index.md, execute
5+
6+
$ python add_meetup_events.py
7+
8+
To over-write with latest output, execute
9+
10+
$ python add_meetup_events.py --rewrite
11+
"""
12+
import httpx
13+
from bs4 import BeautifulSoup
14+
from collections import defaultdict
15+
from datetime import datetime
16+
from slugify import slugify
17+
from jinja2 import Environment
18+
import sys
19+
from pathlib import Path
20+
21+
22+
root = Path(__file__).parent.resolve()
23+
24+
# past events meetup URL
25+
url = "https://www.meetup.com/bostonpython/events/past/"
26+
27+
# markdown file template saved under past-events
28+
MD = """---
29+
title: {{ title }}
30+
sidebar_link: false
31+
---
32+
33+
{{ event_date }}
34+
{% for c in contents %}
35+
{{ c }}
36+
{% endfor %}
37+
38+
Meetup link: [{{ event_url }}]({{ event_url }})
39+
40+
[Back to Past Events Page](index.md)
41+
"""
42+
43+
# list of all events which at the end are joined to contruct
44+
# the index.md file inside past-events folder
45+
past_events = [
46+
"---",
47+
"title: Past Events",
48+
"sidebar_link: false",
49+
"---\n",
50+
"List of past events:\n",
51+
]
52+
53+
54+
if __name__ == "__main__":
55+
try:
56+
# by default this get only gets the 10 most recent events
57+
r = httpx.get(url)
58+
except httpx.RequestError as exc:
59+
print(f"An error occurred while requesting {exc.request.url!r}.")
60+
raise
61+
62+
soup = BeautifulSoup(r.content.decode('utf-8','ignore'), "lxml")
63+
64+
datemap = defaultdict(list)
65+
66+
# beautifulsoup finding the list of recent 10 events
67+
# mapping to defaultdict datemap
68+
lis = soup.find_all("li", {"class": "list-item border--none"})
69+
for li in lis:
70+
t = li.find("time")
71+
d = datetime.fromtimestamp(int(t["datetime"][:10]))
72+
year = d.year
73+
datemap[year].append(li)
74+
75+
# get list of already existing event files under past-events
76+
processed = Path('past-events').glob('**/*')
77+
processed_files = [x for x in processed if x.is_file()]
78+
79+
# structure of data for existing_events
80+
# {
81+
# 2007: {
82+
# datetime.date(2007, 5, 23): [
83+
# ('The Cambridge Python May Meetup', '20070523-the-cambridge-python-may-meetup.md')
84+
# ]
85+
# }
86+
# }
87+
existing_events = defaultdict(lambda: defaultdict(list))
88+
89+
# iterate all the files under past-events and populate
90+
# existing_events
91+
for p in processed_files:
92+
if p.name == "index.md":
93+
continue
94+
event_date = datetime.strptime(p.name.split("-")[0], "%Y%m%d").date()
95+
lines = p.read_text(encoding="utf-8").split("\n")
96+
title = None
97+
for line in lines:
98+
if line.startswith("title:"):
99+
_, title = line.split(": ")
100+
break
101+
existing_events[event_date.year][event_date].append((title, p.name))
102+
103+
last_processed_year = max(existing_events.keys())
104+
max_processed_date = max(existing_events[last_processed_year])
105+
106+
# now iterate over data downloaded from meetup and check whats not been processed
107+
# and saved under past-events
108+
for year, events in datemap.items():
109+
for event in events:
110+
link = event.find("a")
111+
url = link["href"]
112+
title = link.text
113+
parts = [part.strip() for part in title.split(":")]
114+
title = " - ".join(parts)
115+
if title == "Monday office hour":
116+
continue
117+
t = event.find("time")
118+
d = datetime.fromtimestamp(int(t["datetime"][:10]))
119+
if d.date() <= max_processed_date:
120+
continue
121+
name = slugify(title)
122+
filename = f"past-events/{d.strftime('%Y%m%d')}-{name}.md"
123+
location = event.find("div", {"class": "venueDisplay"})
124+
if not location:
125+
location = event.find("p", {"class": "venueDisplay"})
126+
attendees = event.find("li", {"class": "avatarRow--attendingCount"})
127+
try:
128+
count = attendees.text
129+
except:
130+
count = 0
131+
contents = event.find_all("p", {"class": "text--small"})
132+
new_contents = []
133+
for content in contents:
134+
if 'class' in content.attrs:
135+
del content.attrs['class']
136+
if 'style' in content.attrs:
137+
del content.attrs['style']
138+
new_contents.append(content)
139+
140+
event_date = d.strftime("%B %d, %Y")
141+
mydict = {
142+
"title": title,
143+
"event_date": event_date,
144+
"location": location.text,
145+
"event_url": "https://www.meetup.com"+url,
146+
"contents": new_contents,
147+
}
148+
# write event file to past-events
149+
with open(filename, "w") as e:
150+
output = Environment().from_string(MD).render(**mydict).replace('\xa0', '')
151+
e.write(output)
152+
print(f"Write {filename}")
153+
# add to existing_events data structure
154+
existing_events[year][d.date()].append((title, f"{d.strftime('%Y%m%d')}-{name}.md"))
155+
156+
# Now go over all existing_events and construct past_events list
157+
for year in reversed(sorted(existing_events.keys())):
158+
past_events.append(f"- {year}")
159+
for dts in reversed(sorted(existing_events[year].keys())):
160+
for title, filename in sorted(existing_events[year][dts], key=lambda x:x[0]):
161+
past_events.append(f" - [{title}]({filename}) ({dts:%m/%d/%Y})")
162+
163+
# either print or over-write past-event/index.md file
164+
if "--rewrite" in sys.argv:
165+
index_file = root / "past-events" / "index.md"
166+
index_txt = "\n".join(past_events).strip()
167+
index_file.open("w").write(index_txt)
168+
else:
169+
print("\n".join(past_events))

events.md

+2
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ Boston Python runs four kinds of events:
1212
- [**Office hour**](officehour.md) is a weekly hour to drop in and ask anything on your mind.
1313

1414
Specific events are listed on the [Boston Python meetup page](http://bostonpython.com). RSVP there to be sure to get a spot.
15+
16+
You can see our past event [here](past-events/index.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: The Cambridge Python May Meetup
3+
sidebar_link: false
4+
---
5+
6+
May 23, 2007
7+
8+
9+
Needs a location
10+
11+
   
12+
13+
(PLEASE NOTE DATE CHANGE. Sorry for the inconvenience. See details below.)
14+
15+
Why did you choose Python?
16+
17+
Developers and their companies are using Python to do all kinds of tasks. From utilities to ERP systems. Why are they choosing Python over Java, C++ and other proven enterprise 3 and 4gls?
18+
19+
At this meeting we'll all get a chance to meet each other, learn why we've made this choice and what we've learned in the process.
20+
21+
To kick off the meeting, I have asked George Lambert of Goldenware Travel to explain why his company has switched to Python and the benefits and challenges they are seeing.
22+
23+
The meeting will be held at my office in Somerville. We currently have 7 RSVP's and 10 "Maybe's". Of course that may change with the date change.
24+
25+
26+
Meetup link: [https://www.meetup.com/bostonpython/events/5643943/](https://www.meetup.com/bostonpython/events/5643943/)
27+
28+
[Back to Past Events Page](index.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
title: The Cambridge Python June Meetup
3+
sidebar_link: false
4+
---
5+
6+
June 20, 2007
7+
8+
9+
Needs a location
10+
11+
   
12+
13+
Members, Python Newbies and Certified Snake Charmers,
14+
15+
Hope you can all make it to the next Python Meetup. This is a busy month for those of us interested in Python. So for those that can't make some of the events scheduled (pythoncalendar.inav2.com) we hope to review some of the events highlights.
16+
17+
This month's agenda: News and Events, Lighting Talks, General Discussion
18+
19+
News and Events:
20+
21+
Event/Overview/Event Info
22+
Django June / P. Marquez / (DjangoJune.inav2.com)
23+
Mass TLC: / Speaker? / (Masstlc.inav2.com)
24+
WebCamp Boston / Speaker? / (WebCampBoston.inav2.com)
25+
26+
Lighting Talks
27+
1: George Lambert, Goldenware Technology
28+
2: Mike Pittaro, SnapLogic open source data integration Project implemented in 100% Python
29+
30+
General
31+
Open Discussion
32+
33+
Please let us know if you'd like to speak on one of these current events or do a Lighting Talk on a current project, technical problem, solution or innovation.
34+
35+
Look forward to seeing you all. We will confirm at this meeting whether we want Google (in the form of an engineer and a recruiter) to come speak to us or not.
36+
37+
Drop me a line if you have any questions or suggestions.
38+
39+
Peter
40+
41+
42+
Meetup link: [https://www.meetup.com/bostonpython/events/5811659/](https://www.meetup.com/bostonpython/events/5811659/)
43+
44+
[Back to Past Events Page](index.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
title: The Cambridge Python July Meetup
3+
sidebar_link: false
4+
---
5+
6+
July 18, 2007
7+
8+
9+
Needs a location
10+
11+
   
12+
13+
Members, Python Newbies, Certified Snake Charmers and this month Ruby-Lovers!
14+
15+
Hope you can all make it to the next Python Meetup when Special Guest Chris Bowen from Microsoft will talk about Python's role in Microsoft's SilverLight.
16+
17+
Silverlight aims to compete with Adobe Flash and the presentation components of Ajax. It supports Python and Ruby so this would be a good session for all those Ruby-lovers out there.
18+
19+
If you'd like to learn more about Silverlight before the meeting check out these links to voidspace techie blog and Wikipedia for more info.
20+
21+
http://en.wikipedia.o... (http://en.wikipedia.org/wiki/Microsoft_Silverlight)
22+
http://www.voidspace.... (http://www.voidspace.org.uk/python/weblog/arch_d7_2007_04_28.shtml)
23+
24+
This month's agenda: News and Events, Microsoft Silverlight, General Discussion
25+
26+
Please let us know if you'd like to do a Lighting Talk on a current project, technical problem, solution or innovation at a future meeting.
27+
28+
Look forward to seeing you all.
29+
30+
Drop me a line if you have any questions or suggestions.
31+
32+
Peter
33+
34+
35+
Meetup link: [https://www.meetup.com/bostonpython/events/5915248/](https://www.meetup.com/bostonpython/events/5915248/)
36+
37+
[Back to Past Events Page](index.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: The Cambridge Python August Meetup
3+
sidebar_link: false
4+
---
5+
6+
August 15, 2007
7+
8+
9+
Needs a location
10+
11+
   
12+
13+
Fellow Snake Charmers,
14+
15+
Data access is usually where the rubber meets the road. This Meetup will focus who we get data in and out of Python applications.
16+
17+
Be prepared to discuss you best and worst experiences -- querying minds want to know.
18+
19+
In keeping with our discussion, the feature presentation is on STORM.
20+
21+
What is Storm?
22+
23+
Storm is an object-relational mapper (ORM) for Python developed at Canonical. The project has been in development for more than a year for use in Canonical projects such as Launchpad, and has recently been released as an open-source product.
24+
25+
Christopher Armstrong of Canonical will walk us through this open source project. Sometimes called the International Man of Twistery, Christopher is also involved in the Twisted project. This should be a great presentation.
26+
27+
Drop me a line if you have any questions.
28+
29+
Best Regards!
30+
31+
Peter
32+
33+
34+
Meetup link: [https://www.meetup.com/bostonpython/events/6044211/](https://www.meetup.com/bostonpython/events/6044211/)
35+
36+
[Back to Past Events Page](index.md)

0 commit comments

Comments
 (0)