A custom-built Python
Static Site-Generator(SSG) used to generate pages for my personal website and blog. It's used to fulfil the "back-end" needs of my website hosted here on GitHub Pages.
Called "Monochrome" because it was for being built for a website that was rendered using mostly black and white, and a hint of grey. However, before I could finish this script, I added a dark-mode that's got a lovely shade of pink as the highlight colour.
The name stuck due to the argument that the pink is the only "chrome" in the surrounding greyscale. Though it was mostly because I could not think of anything else.
The code was inspired by Jahongir Rahmonov's tutorial.
os
datetime
markdown2
jinja2
tkinter
The markdown files need to begin with certain YAML style metadata that will provide the SSG the necessary information to style the page.
Each file will begin with the following block of information:
---
page-title: {{Title of the page}}
page-description: {{Description of the page}}
main-class: {{CSS class to be used}}
title: {{Title of the blog post}}
date: {{Date of the blog post}}
tags: {{Tags relating to the blog post}}
thumbnail: {{Thumbnail image}}
summary: {{Summary of the page}}
slug: {{Filepath to the post}}
---
{{Blank line}}
*The block must be followed by an empty line.*
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>{{post.title}}</h1>
<small>{{post.date}}</small>
{{post.content}}
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
{% for post in posts %}
<p>
<h2>{{loop.index}}: <a href="/blog/posts/{{ post.slug }}.html">{{post.title}}</a> <small>{{post.date}}</small></h2>
{{post.summary}}
</p>
{% endfor %}
</body>
</html>
.
├── drafts
│ ├── crazy-ideas.md
│ └── writers-block.md
├── content
│ ├── 2022-09-10-arson.md
│ └── 2022-19-11-boiled-egg.md
├── posts
│ ├── arson-crime-or-hobby.html
│ └── my-life-as-a-boiled-egg.html
├── template
│ ├── all-posts-template.html
│ |── blog-template.html
| └── recent-posts-template.html
├── all-posts.html # list of all posts of the blog
└── index.html # contains only 5 recent posts in case of my blog
import os
from datetime import datetime
from jinja2 import Environment, PackageLoader
from markdown2 import markdown
from tkinter import *
from tkinter import filedialog
label_file_explorer = object
def inputFile():
# Asks user to select the source file to be converted.
# Supports .md and .txt written in Markdown format
global input_filename
input_filename = filedialog.askopenfilename(
initialdir= ".",
filetypes= (
("All files", "*.*"),
("Markdown files","*.md"),
("Text files", "*.txt")
)
)
label_file_explorer.configure(text="File Opened: "+input_filename)
def newBlog():
with open(input_filename, 'r') as file:
parsed_md = markdown(file.read(), extras=['metadata'])
env = Environment(loader=PackageLoader('package', 'templates'))
# seems to work only with a blank package.py file in the project root.
post_detail_template = env.get_template('post-detail.html')
data = {
'content': parsed_md,
'title': parsed_md.metadata['title'],
'date': parsed_md.metadata['date']
}
blog_html_content = post_detail_template.render(post=parsed_md.metadata)
blog_filepath = 'output/posts/{slug}.html'.format(slug=parsed_md.metadata['slug'])
with open('blog_filepath', 'w') as file:
file.write(blog_html_content)
label_file_explorer.configure(text="Blog Rendered")
def indexUpdate():
BLOG = {}
label_file_explorer.configure(text="Updating index...")
for blog_md in os.listdir('content'):
file_path = os.path.join('content', blog_md)
with open(file_path, 'r') as file:
BLOG[blog_md] = markdown(file.read(),extras=['metadata'])
BLOG = {
post: BLOG[post] for post in sorted(
BLOG, key=lambda post: datetime.strptime(
BLOG[post].metadata['date'], '%Y-%m-%d'),
reverse=True
)
}
env = Environment(loader=PackageLoader('monochrome', 'templates'))
all_posts_template = env.get_template('all-posts-template.html')
recent_posts_template = env.get_template('recent-posts-template.html')
index_blog_metadata = [
BLOG[post].metadata for post in BLOG
]
all_posts_html = all_posts_template.render(posts=index_blog_metadata)
recents = []
blogsize = len(index_blog_metadata)
if blogsize < 5:
r = blogsize
else:
r = 5
i=0
while i < r:
recents.append(index_blog_metadata[i])
i = i + 1
recent_posts_html = all_posts_template.render(posts=recents)
with open('output/all.html', 'w') as file:
file.write(all_posts_html)
with open('output/index.html', 'w') as file:
file.write(recent_posts_html)
label_file_explorer.configure(text="Index Updated")
Using TKinter
window = Tk()
window.title('Monochrome SSG v-01')
window.geometry("500x200")
window.config(background = "white")
label_file_explorer = Label(window,
text = "Please Select the Input file",
width= 60,
fg = "black",
bg="white")
button_inputFile = Button(window,
text = "Input File",
command = inputFile)
button_newBlog = Button(window,
text = "Render Blog",
command = newBlog)
button_indexUpdate = Button(window,
text = "Update Index",
command = indexUpdate)
button_exit = Button(window,
text = "Exit",
command = exit)
label_file_explorer.grid(column = 1, row = 1)
button_inputFile.grid(column = 1, row = 2)
button_newBlog.grid(column = 1, row = 3)
button_indexUpdate.grid(column = 1, row = 4)
button_exit.grid(column = 1, row = 5)
window.mainloop()
The GUI currently looks like this:
.ipynb
HTML export.