Commit 694c4e3f authored by berumuron's avatar berumuron
Browse files

add: Add support for series

parents 20af2ee3 0542b872
......@@ -42,7 +42,7 @@ class ProgramError(Exception):
class Article:
def __init__(self, slug, article_content, configuration={}):
def __init__(self, slug, article_content, configuration={}, serie=None):
# We convert the file's content into HTML
md = markdown.Markdown(extensions=["meta", "fenced_code", "attr_list"])
content = md.convert(article_content)
......@@ -62,6 +62,7 @@ class Article:
else:
self.meta[article_key] = values[0]
self.meta["ARTICLE_CONTENT"] = content
self.meta["ARTICLE_SERIE"] = serie
# And we initialize a template for the article
article_template_filepath = os.path.join(
......@@ -118,6 +119,9 @@ class Article:
)
return article_update.astimezone(tz=self.timezone())
def serie(self):
return self.meta["ARTICLE_SERIE"]
class Page:
def __init__(self, slug, page_content, configuration={}):
......@@ -150,10 +154,16 @@ class Page:
def slug(self):
return self.meta["PAGE_SLUG"]
def title(self):
return self.meta.get("PAGE_TITLE", "A page")
def url(self):
site_url = self.meta.get("SITE_URL", "")
return f"{site_url}{self.slug()}.html"
def uuid(self):
return str(uuid.uuid5(uuid.NAMESPACE_URL, self.url()))
def content(self):
return self.meta["PAGE_CONTENT"]
......@@ -170,6 +180,24 @@ def extract_yaml_configuration(content):
return {}, content
def list_dirs(path):
"""List all the directories within a path.
"""
for filename in os.listdir(path):
full_path = os.path.join(path, filename)
if os.path.isdir(full_path):
yield filename
def list_files(path):
"""List all the files within a path.
"""
for filename in os.listdir(path):
full_path = os.path.join(path, filename)
if os.path.isfile(full_path):
yield filename
def dir_tree(path):
"""List all the tree directory under the given path.
"""
......@@ -242,12 +270,16 @@ def main(environment):
configuration_path = os.path.join(os.curdir, "configuration.yml")
configuration = load_configuration(configuration_path, output_path)
# Check that index file exists and load a Page from it
# STEP 1: we load all the Articles and Pages
pages = []
articles = []
articles_by_series = {}
# Check that index file exists
index_filepath = os.path.join(os.curdir, "index.html")
if not os.path.exists(index_filepath):
raise ProgramError("./index.html file does not exist")
pages = []
with open(index_filepath, "r") as index_file:
page = Page("index", index_file.read(), configuration=configuration)
pages.append(page)
......@@ -267,7 +299,63 @@ def main(environment):
page = Page(slug, page_file.read(), configuration=configuration)
pages.append(page)
# And write them in the ./site folder
# Check that articles directory exists and load Articles from it
articles_path = os.path.join(os.curdir, "articles")
if os.path.isdir(articles_path):
# We iterate on the directories of articles dir. If they contain
# "serie.html" file, it means they contain a serie. Otherwise, we list
# articles as usual.
for dirname in list_dirs(articles_path):
dirpath = os.path.join(articles_path, dirname)
serie_filepath = os.path.join(dirpath, "serie.html")
serie = None
if os.path.exists(serie_filepath):
# We have a serie Page! Each article of the directory must be
# considered part of the serie.
slug = f"serie/{dirname}"
with open(serie_filepath, "r") as serie_file:
serie = Page(slug, serie_file.read(), configuration=configuration)
pages.append(serie)
articles_by_series[serie] = []
for filename in list_files(dirpath):
filename_no_ext, ext = os.path.splitext(filename)
slug = os.path.basename(filename_no_ext)
if ext != ".md":
continue
# We read the article file and initialize an Article from it
article_filepath = os.path.join(dirpath, filename)
with open(article_filepath, "r") as article_file:
article = Article(
slug,
article_file.read(),
configuration=configuration,
serie=serie,
)
articles.append(article)
if serie:
articles_by_series[serie].append(article)
for filename in list_files(articles_path):
filename_no_ext, ext = os.path.splitext(filename)
slug = os.path.basename(filename_no_ext)
if ext != ".md":
continue
# We read the article file and initialize an Article from it
article_filepath = os.path.join(articles_path, filename)
with open(article_filepath, "r") as article_file:
article = Article(
slug, article_file.read(), configuration=configuration
)
articles.append(article)
# STEP 2: we write all the pages and articles
for page in pages:
output_filepath = os.path.join(output_path, f"{page.slug()}.html")
......@@ -279,40 +367,10 @@ def main(environment):
output_file.write(page.render())
print(f"Written page: {page.url()}")
# Check that a ./static folder exists and copy its content under the ./site
# folder
static_dirpath = os.path.join(os.curdir, "static")
if os.path.isdir(static_dirpath):
distutils.dir_util.copy_tree(static_dirpath, output_path)
# Check that articles directory exists on the filesystem
articles_path = os.path.join(os.curdir, "articles")
if not os.path.isdir(articles_path):
# If not, just stop here
return
# List files from ./articles
articles = []
for filename in dir_tree(articles_path):
filename_no_ext, ext = os.path.splitext(filename)
if ext != ".md":
# Don't consider files which are not Markdown
continue
slug = os.path.basename(filename_no_ext)
# We read the article file and initialize an Article from it
article_filepath = os.path.join(articles_path, filename)
with open(article_filepath, "r") as article_file:
article = Article(slug, article_file.read(), configuration=configuration)
articles.append(article)
# If there is no articles, stop there
if len(articles) == 0:
return
# Sort the articles by publication date
articles.sort(key=lambda article: article.date(), reverse=True)
for serie_articles in articles_by_series.values():
serie_articles.sort(key=lambda article: article.date(), reverse=True)
# And write them in the ./site folder
for article in articles:
......@@ -333,6 +391,7 @@ def main(environment):
output_file.write(blog_template.render(context))
print(f"Written page: {configuration['SITE_URL']}blog.html")
# STEP 3: we write also the Atom feeds (the main one + the series').
# Just create some variables so it's easier to manipulate after
site_url = configuration["SITE_URL"]
site_uuid = configuration["SITE_UUID"]
......@@ -341,7 +400,10 @@ def main(environment):
timezone = pytz.timezone(site_timezone)
current_datetime = datetime.datetime.now(tz=timezone)
# Build the Atom feed
feed_dirpath = os.path.join(output_path, "feeds")
os.mkdir(feed_dirpath)
# Build the main Atom feed
feed = boopfeed.Atom()
feed.add_header(
{
......@@ -373,11 +435,54 @@ def main(environment):
feed.add_entry(entry)
# And write it on the filesystem
feed_dirpath = os.path.join(output_path, "feeds")
os.mkdir(feed_dirpath)
feed_filepath = os.path.join(feed_dirpath, "all.atom.xml")
feed.write(feed_filepath)
print(f"Written feed: {configuration['SITE_URL']}feeds/all.atom.xml")
print(f"Written feed: {site_url}feeds/all.atom.xml")
# Now, build the feeds for series (same process)
for serie, serie_articles in articles_by_series.items():
feed_filename = f"{os.path.basename(serie.slug())}.atom.xml"
feed_filepath = os.path.join(feed_dirpath, feed_filename)
feed = boopfeed.Atom()
feed.add_header(
{
"title": f"{serie.title()} - {site_title}",
"self_link": f"{site_url}feeds/{feed_filename}",
"site_link": serie.url(),
"id": f"urn:uuid:{serie.uuid()}",
"updated": current_datetime,
}
)
for article in serie_articles:
# and complete it for each article
entry = {
"title": article.title(),
"id": f"urn:uuid:{article.uuid()}",
"article_link": article.url(),
"published": article.date(),
"content": article.content(),
}
article_author = article.author()
if article_author:
entry["author"] = article_author
article_update = article.update()
if article_update:
entry["updated"] = article_update
feed.add_entry(entry)
# And write it on the filesystem
feed.write(feed_filepath)
print(f"Written feed: {site_url}feeds/{feed_filename}")
# STEP 4: and we finish by copying the static folder.
static_dirpath = os.path.join(os.curdir, "static")
if os.path.isdir(static_dirpath):
distutils.dir_util.copy_tree(static_dirpath, output_path)
print(f"Static files copied")
if __name__ == "__main__":
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment