initial commit

This commit is contained in:
Nora Goodman 2025-05-22 18:03:28 -04:00
commit 6ccdb684ec
11 changed files with 496 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
scraper/
.venv/
__pycache__/
posts/

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

164
blog.html Normal file
View file

@ -0,0 +1,164 @@
<html>
<head>
<link rel="stylesheet" href="style.css">
<title>
Rochester Food Not Bombs
</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Teko:wght@300..700&display=swap" rel="stylesheet">
<script>
function start_animation() {
var element = document.getElementById("biglogo")
element.style.animation = 'none';
element.offsetHeight;
element.style.animation = null;
}
</script>
</head>
<body>
<h1>Blog not bombs ;)</h1>
<div class="flex-container">
<div class="post-container fancy-border">
<a href="posts/1739219032.html">
<img src="scraper/DF59SA5h5rr/2025-02-10_20-23-52_UTC_2.webp" width="200rem" alt="post image from Feb 10, &#39;25">
<p>Feb 10, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1739134557.html">
<img src="scraper/DF3cKOQxWCX/2025-02-09_20-55-57_UTC.webp" width="200rem" alt="post image from Feb 9, &#39;25">
<p>Feb 9, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1739125296.html">
<img src="scraper/DF3KfslB3-0/2025-02-09_18-21-36_UTC_2.webp" width="200rem" alt="post image from Feb 9, &#39;25">
<p>Feb 9, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1738861896.html">
<img src="scraper/DFvUGTUIizI/2025-02-06_17-11-36_UTC_1.jpg" width="200rem" alt="post image from Feb 6, &#39;25">
<p>Feb 6, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1738790353.html">
<img src="scraper/DFtLpDuhhdz/2025-02-05_21-19-13_UTC.webp" width="200rem" alt="post image from Feb 5, &#39;25">
<p>Feb 5, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1738614358.html">
<img src="scraper/DFn79SHBIg7/2025-02-03_20-25-58_UTC_1.webp" width="200rem" alt="post image from Feb 3, &#39;25">
<p>Feb 3, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1738512058.html">
<img src="scraper/DFk41j2uCYI/2025-02-02_16-00-58_UTC_1.webp" width="200rem" alt="post image from Feb 2, &#39;25">
<p>Feb 2, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1738360956.html">
<img src="scraper/DFgYocRBVCS/2025-01-31_22-02-36_UTC_1.webp" width="200rem" alt="post image from Jan 31, &#39;25">
<p>Jan 31, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1738179035.html">
<img src="scraper/DFa9pU0hfG6/2025-01-29_19-30-35_UTC_2.webp" width="200rem" alt="post image from Jan 29, &#39;25">
<p>Jan 29, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1738016440.html">
<img src="scraper/DFWHhSdBt0G/2025-01-27_22-20-40_UTC_4.webp" width="200rem" alt="post image from Jan 27, &#39;25">
<p>Jan 27, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1737906963.html">
<img src="scraper/DFS2tbAObAq/2025-01-26_15-56-03_UTC_4.webp" width="200rem" alt="post image from Jan 26, &#39;25">
<p>Jan 26, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1737754417.html">
<img src="scraper/DFOTv_2hNDS/2025-01-24_21-33-37_UTC_2.webp" width="200rem" alt="post image from Jan 24, &#39;25">
<p>Jan 24, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1737665676.html">
<img src="scraper/DFLqfb6h1Sa/2025-01-23_20-54-36_UTC.webp" width="200rem" alt="post image from Jan 23, &#39;25">
<p>Jan 23, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1737575265.html">
<img src="scraper/DFI-C5_Bg48/2025-01-22_19-47-45_UTC.webp" width="200rem" alt="post image from Jan 22, &#39;25">
<p>Jan 22, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1737473510.html">
<img src="scraper/DFF79rCuDuc/2025-01-21_15-31-50_UTC.jpg" width="200rem" alt="post image from Jan 21, &#39;25">
<p>Jan 21, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1737472253.html">
<img src="scraper/DFF5kNlOpwk/2025-01-21_15-10-53_UTC_5.jpg" width="200rem" alt="post image from Jan 21, &#39;25">
<p>Jan 21, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1737385082.html">
<img src="scraper/DFDTTQLR58l/2025-01-20_14-58-02_UTC.jpg" width="200rem" alt="post image from Jan 20, &#39;25">
<p>Jan 20, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1737325433.html">
<img src="scraper/DFBhh3rhuHV/2025-01-19_22-23-53_UTC.jpg" width="200rem" alt="post image from Jan 19, &#39;25">
<p>Jan 19, &#39;25</p>
</a>
</div>
<div class="post-container fancy-border">
<a href="posts/1737308811.html">
<img src="scraper/DFBBjkBOkE8/2025-01-19_17-46-51_UTC.jpg" width="200rem" alt="post image from Jan 19, &#39;25">
<p>Jan 19, &#39;25</p>
</a>
</div>
</div>
</body>
</html>

66
builder.py Normal file
View file

@ -0,0 +1,66 @@
import lzma
import json
import os
import re
from pytz import timezone
from datetime import datetime
import bisect
from jinja2 import Environment, PackageLoader, select_autoescape
scraper_path = 'scraper/'
output_path='./'
env = Environment(
loader=PackageLoader("builder"),
autoescape=select_autoescape()
)
blog_template = env.get_template("blog_template.html")
post_template=env.get_template("post.html")
posts = []
for folder in os.listdir(scraper_path):
if os.path.isdir(os.path.join(scraper_path, folder)):
post = {'pictures': []}
for file in os.listdir(scraper_path + folder):
if file.endswith('.xz'):
if file.endswith('.xz'):
with lzma.open(scraper_path + folder + '/' + file) as f:
json_bytes = f.read()
stri = json_bytes.decode('utf-8')
data = json.loads(stri)
# checks that we're looking at a downloaded post, not the profile picture or something
if 'node' in data.keys() and 'date' in data['node'].keys():
timestamp = data['node']['date']
post['timestamp'] = timestamp #useful for sorting
post['datetime'] = datetime.fromtimestamp(data['node']['date'], timezone("America/New_York")).strftime("%b %e, '%y")
# use negative timestamp because that's the easiest way to sort from high to low
bisect.insort_right(posts, post, key=lambda t: -t['timestamp'])
if file.endswith('.webp') or file.endswith('.jpg'):
filepath=os.path.join(scraper_path, folder, file)
def get_index(t:str):
extension_len = 6
if t.endswith('.jpg'):
extension_len = 5
lastpart = filepath[filepath.rfind('_'):-extension_len] # will get the index of the image within the post if more than one
if lastpart.isdigit():
return int(lastpart)
else:
return 0
bisect.insort_left(post['pictures'], filepath, key=get_index)
if file.endswith('.txt'):
filepath=os.path.join(scraper_path, folder, file)
with open(filepath, 'r') as file:
post['caption'] = file.read()
if ('timestamp' in post.keys()):
with open(output_path + 'posts/' + str(post['timestamp']) +'.html', 'w+') as output_file:
output_file.write(post_template.render(post=post))
homepage_template = env.get_template("index.html")
with open(output_path + 'index.html', "w+") as output_file:
output_file.write(homepage_template.render())
with open(output_path + 'blog.html', "w+") as output_file:
output_file.write(blog_template.render(posts=posts))

77
index.html Normal file
View file

@ -0,0 +1,77 @@
<html>
<head>
<link rel="stylesheet" href="style.css">
<title>
Rochester Food Not Bombs
</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Teko:wght@300..700&display=swap" rel="stylesheet">
<script>
function start_animation() {
var element = document.getElementById("biglogo")
element.style.animation = 'none';
element.offsetHeight;
element.style.animation = null;
}
</script>
</head>
<body>
<img id="biglogo" class="spinny" onclick="start_animation()" src="assets/logo.png" alt="logo">
<h1>Rochester Food Not Bombs!</h1>
Solidarity not charity: From Rochester, New York.
<p>Free hot meals are served every Saturday at 6:30PM in front of the <a
href="https://maps.app.goo.gl/gDNseCg1RQyeLKXF8">RTS Transit Center on St. Paul. </a></p>
<p>Help us cook every Saturday at 4PM at the <a href="http://thesquirrel.org/">Flying Squirrel Community Space.</a>
</p>
<div class="flex-container">
<div id="resources-box" class="fancy-border">
<h2>Quick resources</h2>
<ul id="resources-list">
<li><a href="https://docs.google.com/document/d/1UcoQ984Qwq22aR8YgyVk76VepxxFZM1QNllVeaHzWIs/edit?tab=t.0">List
of food stands</a></li>
<li><a href="https://docs.google.com/document/d/1UcoQ984Qwq22aR8YgyVk76VepxxFZM1QNllVeaHzWIs/edit?tab=t.0">List
of free food stands</a></li>
<li><a
href="https://www.google.com/maps/d/u/0/viewer?ll=43.19335689697014%2C-77.66649769973144&z=12&mid=1hGQ70VxoHncH6ardfHs6uh3HcrCp7iI">Map
of free food stands</a></li>
<li>Venmo: @rocfoodnotbombs</li>
<li>Email: <a href="mailto:RocFoodNotBombs@proton.me">RocFoodNotBombs@proton.me</a></li>
<li>Instagram: <a href="https://www.instagram.com/rocfnb/">@rocfnb</a></li>
</ul>
</div>
<div id="blurb" class="fancy-border">
<p>Rochester Food Not Bombs recovers resources to create free vegetarian and vegan meals in our local
community.
We
are a decentralized all-volunteer run group, with no hierarchy or formal leaders, making decisions based
on
consensus. </p>
<p>
We recognize poverty as a form of violence, and think access to food should be viewed as a right, rather
than a
privilege. In this practice, we strive to reduce waste by turning donated food into productive meals. We
source
food from vendors at the Public Market, as well as relying on donations. Food should be a source of
nutrition
for
people, not profit under capitalism. Through Community organizing and outreach, we support other local
and
national peace and justice groups to create a broader sense of social responsibility -- If you would
like to
get
involved, please reach out to us!
</p>
</div>
</div>
<h2><a href="blog.html">See what we're up to :-)</a></h1>
</body>
</html>

65
style.css Normal file
View file

@ -0,0 +1,65 @@
body {
background-color: #EEEEEE;
text-align: center;
}
#resources-list {
text-align: left;
}
.flex-container {
display: flex;
flex-wrap: wrap;
margin: auto;
align-items: center;
justify-content: center;
}
.fancy-border {
border-width: 0.2em;
border-style: ridge;
border-radius: 1em;
}
#resources-box {
width: 20em;
border-color: #b9539f;
margin: auto;
margin-left: 1rem;
margin-right: 1rem;
margin-top: 2rem;
padding: 1em;
}
#blurb {
width: 40em;
border-color: #f27322;
margin: auto;
margin-top: 2rem;
margin-left: 1rem;
margin-right: 1rem;
text-align: left;
padding: 1em 2em 1em 2em;
}
:is(h1, h2, h3, h4, h5, h6) {
font-family: "Teko", serif;
font-optical-sizing: auto;
font-style: normal;
}
@keyframes logospin {
from {
transform: rotate(-2turn);
}
to {
transform: rotate(0);
}
}
.spinny {
animation-name: logospin;
animation-duration: 2s;
}
.post-container{
padding: 0.5rem;
margin: 0.5rem;
border-color: black;
}

27
templates/base.html Normal file
View file

@ -0,0 +1,27 @@
<html>
<head>
<link rel="stylesheet" href="style.css">
<title>
Rochester Food Not Bombs
</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Teko:wght@300..700&display=swap" rel="stylesheet">
<script>
function start_animation() {
var element = document.getElementById("biglogo")
element.style.animation = 'none';
element.offsetHeight;
element.style.animation = null;
}
</script>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>

View file

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block content %}
<h1>Blog not bombs ;)</h1>
<div class="flex-container">
{% for post in posts %}
<div class="post-container fancy-border">
<a href="posts/{{ post['timestamp'] }}.html">
<img src="{{ post['pictures'][0] }}" width="200rem" alt="post image from {{ post['datetime'] }}">
<p>{{ post['datetime'] }}</p>
</a>
</div>
{% endfor %}
</div>
{% endblock %}

53
templates/index.html Normal file
View file

@ -0,0 +1,53 @@
{% extends "base.html" %}
{% block content %}
<img id="biglogo" class="spinny" onclick="start_animation()" src="assets/logo.png" alt="logo">
<h1>Rochester Food Not Bombs!</h1>
Solidarity not charity: From Rochester, New York.
<p>Free hot meals are served every Saturday at 6:30PM in front of the <a
href="https://maps.app.goo.gl/gDNseCg1RQyeLKXF8">RTS Transit Center on St. Paul. </a></p>
<p>Help us cook every Saturday at 4PM at the <a href="http://thesquirrel.org/">Flying Squirrel Community Space.</a>
</p>
<div class="flex-container">
<div id="resources-box" class="fancy-border">
<h2>Quick resources</h2>
<ul id="resources-list">
<li><a href="https://docs.google.com/document/d/1UcoQ984Qwq22aR8YgyVk76VepxxFZM1QNllVeaHzWIs/edit?tab=t.0">List
of food stands</a></li>
<li><a href="https://docs.google.com/document/d/1UcoQ984Qwq22aR8YgyVk76VepxxFZM1QNllVeaHzWIs/edit?tab=t.0">List
of free food stands</a></li>
<li><a
href="https://www.google.com/maps/d/u/0/viewer?ll=43.19335689697014%2C-77.66649769973144&z=12&mid=1hGQ70VxoHncH6ardfHs6uh3HcrCp7iI">Map
of free food stands</a></li>
<li>Venmo: @rocfoodnotbombs</li>
<li>Email: <a href="mailto:RocFoodNotBombs@proton.me">RocFoodNotBombs@proton.me</a></li>
<li>Instagram: <a href="https://www.instagram.com/rocfnb/">@rocfnb</a></li>
</ul>
</div>
<div id="blurb" class="fancy-border">
<p>Rochester Food Not Bombs recovers resources to create free vegetarian and vegan meals in our local
community.
We
are a decentralized all-volunteer run group, with no hierarchy or formal leaders, making decisions based
on
consensus. </p>
<p>
We recognize poverty as a form of violence, and think access to food should be viewed as a right, rather
than a
privilege. In this practice, we strive to reduce waste by turning donated food into productive meals. We
source
food from vendors at the Public Market, as well as relying on donations. Food should be a source of
nutrition
for
people, not profit under capitalism. Through Community organizing and outreach, we support other local
and
national peace and justice groups to create a broader sense of social responsibility -- If you would
like to
get
involved, please reach out to us!
</p>
</div>
</div>
<h2><a href="blog.html">See what we're up to :-)</a></h1>
{% endblock %}

16
templates/post.html Normal file
View file

@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block content %}
<h1>{{ post['datetime'] }}</h1>
{% for paragraph in post['caption'].split('\n') %}
<p>{{ paragraph }}</p>
{% endfor %}
<ul>
{% for picture in post['pictures'] %}
<li>
<img src="../{{ picture }}" width="400rem">
</li>
{% endfor %}
</ul>
{% endblock %}

10
templates/rss.xml Normal file
View file

@ -0,0 +1,10 @@
<rss version="2.0">
<channel>
<title>Rochester Food Not Bombs</title>
<link>https://rocfoodnotbombs.com/blog.html</link>
<description>
Updates on the Rochester Food not Bombs chapter, automatically mirrored from Instagram.
</description>
<language>en-us</language>
<lastBuildDate>{{ date }}</lastBuildDate>
{% for %}