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 = '' 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())