import rfeed
from datetime import datetime
from .common import SiteConfig
from .articles import ArticleMetadata, Article, format_article_tags
from .templating import format_html_template, TemplateSelections
def build_tag_index(index: dict[str, Article], wildcard ='*') -> dict[str, list[Article]]:
'''Creates an inverted index mapping each article tag to a postings list of articles
with said tag.'''
tag_index = {}
# Index the articles in ascending order of original publication date.
for article in sorted(index.values(), key = lambda a: a.metadata.date):
# Add all articles to the wildcard tag's postings list.
if wildcard is not None:
tag_index[wildcard] = (tag_index.get(wildcard,[])) + [article]
# Add the article to each of its tags' posting lists.
for tag in article.metadata.tags:
tag_index[tag] = (tag_index.get(tag,[])) + [article]
return tag_index
def build_blog_archive(
site: SiteConfig,
index: dict[str, Article],
template_selections: TemplateSelections,
**kwargs
) -> str:
'''Converts an index, formatted as filestem: (metadata, contents) dict,
into an HTML page containing the list of articles, sorted from newest to oldest.
Note: partials must be expanded into the kwargs, as they are needed to generate
the overall page.
'''
# Add each article as a list item to an unordered list.
archive_html_list = '
'
for article in sorted(index.values(), key = lambda a: a.metadata.date)[::-1]:
# Generate HTML for the article (including metadata tags).
archive_html_list += format_html_template(
template_selections['archive_li'],
article_filestem = article,
blog_tags = ' '.join(format_article_tags(article.metadata.tags, template_selections['tag'], site = site)),
article = article,
site = site,
**kwargs
)
archive_html_list +='
'
tag_index = build_tag_index(index)
tag_selector_options = []
tag_selector_css_rules = [f'''
body:has(input[name="tag-selector"][value="*"]:checked) li:has(.blog-tag){{{{
display: list-item!important;
}}}}
''']
# Add tag selector options in descending order of article count.
for tag, articles in sorted(tag_index.items(), key = lambda item : -len(item[1])):
tag_selector_options.append(format_html_template(
template_selections['tag_selector_option'],
tag_name = tag,
number_with_tag = len(articles),
site = site,
**kwargs
))
if tag == '*':
continue
tag_selector_css_rules.append(f'''
body:has(input[name="tag-selector"]:not([value="{tag}"]):checked) li:has(.blog-tag[data="{tag}"]){{{{
display: none;
}}}}
body:has(input[name="tag-selector"][value="{tag}"]:checked) li:has(.blog-tag[data="{tag}"]){{{{
display: list-item!important;
}}}}
''')
# For Python 3.9-friendliness.
newline = '\n'
# Generate the archive article.
archive_html_article = format_html_template(
template_selections['archive_article'],
content = archive_html_list,
tag_selector_options = ' '.join(tag_selector_options),
tag_selector_css_rules = f'',
site = site,
**kwargs
)
# Interpolate the article into the overall page template.
archive_html_page = format_html_template(
template_selections['page'],
content = archive_html_article,
site = site,
**kwargs
)
with open(f"{site.web_root.rstrip('/')}/archive.html", 'w') as f:
f.write(archive_html_page)
def build_rss_feed(site: SiteConfig, index: dict[str, Article]):
feed = rfeed.Feed(
title = site.title,
link = f"{site.base_url.rstrip('/')}/rss.xml",
description = site.description,
language = "en-US",
lastBuildDate = datetime.now(),
items = [
rfeed.Item(
title = article.metadata.title,
link = f"{site.base_url.rstrip('/')}/{article.path}",
description = article.metadata.description,
author = article.metadata.author,
guid = rfeed.Guid(article.path),
pubDate = datetime(
article.metadata.date.year,
article.metadata.date.month,
article.metadata.date.day
)
)
for article in index.values()
]
)
with open(f"{site.web_root.rstrip('/')}/rss.xml", 'w') as f:
f.write(feed.rss())