<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Weblog by Lars Steinke</title><link>https://blog.lsweb.eu/</link><description>Recent posts from the blog.</description><generator>Hugo</generator><language>en</language><lastBuildDate>Mon, 02 Nov 2026 07:09:05 +0000</lastBuildDate><atom:link href="https://blog.lsweb.eu/rss.xml" rel="self" type="application/rss+xml"/><item><title>Migrating from Mezzanine content management system to static page generation</title><link>https://blog.lsweb.eu/blog/migrate-from-mezzanine-to-static-page-generation/</link><pubDate>Mon, 02 Nov 2026 07:09:05 +0000</pubDate><guid>https://blog.lsweb.eu/blog/migrate-from-mezzanine-to-static-page-generation/</guid><description>&lt;p&gt;As the effort of keeping a complex Python software stack serviced outweighed the outcome, I migrated my little weblog to a static website generator (&lt;a href="https://gohugo.io" target="_blank" rel="noopener noreferrer" title="Hugo"&gt;Hugo&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Content conversion was assisted by agentic engineering (using &lt;a href="https://github.com/glennvdv/opencode-dockerized" target="_blank" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt;) and the resulting conversion script is provided here for whomever it concerns.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Export the Mezzanine/Django fixtures to JSON (I chose to focus on the blog content solely): &lt;code&gt;python manage.py dumpdata --indent 2 --exclude auth.permission --exclude sessions --exclude admin.LogEntry --exclude django_comments --exclude auth.group --exclude twitter --natural-foreign --natural-primary generic.keyword generic.assignedkeyword pages blog galleries redirects &amp;gt; dump_431_excerpt-202610.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Covert the HTML content (with tags/categories) from the JSON export (in &lt;code&gt;01_export&lt;/code&gt;) to Markdown for Hugo (in &lt;code&gt;hugo/content&lt;/code&gt;), using this Python script (with &lt;code&gt;dependencies = [&amp;quot;beautifulsoup4&amp;gt;=4.13,&amp;lt;5&amp;quot;, &amp;quot;markdownify&amp;gt;=1.1,&amp;lt;2&amp;quot;]&lt;/code&gt;):&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;One-time import of the Mezzanine fixture and local media into Hugo.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; __future__ &lt;span style="color:#f92672"&gt;import&lt;/span&gt; annotations
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; re
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; shutil
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; collections &lt;span style="color:#f92672"&gt;import&lt;/span&gt; defaultdict
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; datetime &lt;span style="color:#f92672"&gt;import&lt;/span&gt; datetime
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pathlib &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Path, PurePosixPath
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; textwrap &lt;span style="color:#f92672"&gt;import&lt;/span&gt; dedent
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Any
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; bs4 &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BeautifulSoup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; markdownify &lt;span style="color:#f92672"&gt;import&lt;/span&gt; markdownify
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ROOT &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Path(__file__)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;resolve()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parents[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FIXTURE &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ROOT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;01_export/dump_431_excerpt-202610.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SOURCE_MEDIA &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ROOT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;01_export/blog/media&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SITE &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ROOT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;hugo&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CONTENT &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SITE &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PUBLISHED_STATUS &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Confirmed against the live site&amp;#39;s published posts.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AUTHOR_NAME &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Lars Steinke&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Public display name shown by the live site.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;toml_value&lt;/span&gt;(value: Any) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; str:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; isinstance(value, bool):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; value &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;false&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; isinstance(value, str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; json&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dumps(value, ensure_ascii&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; isinstance(value, (int, float)):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; str(value)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; isinstance(value, list):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;[&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(toml_value(item) &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; item &lt;span style="color:#f92672"&gt;in&lt;/span&gt; value) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TypeError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Unsupported TOML value: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;type(value)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;__name__&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;write_content&lt;/span&gt;(path: Path, fields: dict[str, Any], body: str) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parent&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mkdir(parents&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, exist_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; front_matter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;+++&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;key&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; = &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;toml_value(value)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; key, value &lt;span style="color:#f92672"&gt;in&lt;/span&gt; fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;items() &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; value &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;write_text(front_matter &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;+++&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; body&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strip() &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, encoding&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;normalize_html&lt;/span&gt;(body: str) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; str:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body&lt;span style="color:#f92672"&gt;.&lt;/span&gt;replace(&lt;span style="color:#e6db74"&gt;&amp;#34;/static/media/uploads/&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;/media/uploads/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;replace(&lt;span style="color:#e6db74"&gt;&amp;#34;/static/img/&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;/img/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gpg_form &lt;span style="color:#f92672"&gt;=&lt;/span&gt; re&lt;span style="color:#f92672"&gt;.&lt;/span&gt;compile(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;r&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;form\b(?=[^&amp;gt;]*action=[&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;]https://pgp\.cs\.uu\.nl/mk_path\.cgi[&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;])[^&amp;gt;]*&amp;gt;.*?&amp;lt;/form&amp;gt;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; re&lt;span style="color:#f92672"&gt;.&lt;/span&gt;IGNORECASE &lt;span style="color:#f92672"&gt;|&lt;/span&gt; re&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DOTALL,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; gpg_form&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sub(&lt;span style="color:#e6db74"&gt;&amp;#34;{{&amp;lt; gpg-trust-path &amp;gt;}}&amp;#34;&lt;/span&gt;, body)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;normalize_blog_links&lt;/span&gt;(body: str, post_slugs: set[str]) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; str:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; link &lt;span style="color:#f92672"&gt;=&lt;/span&gt; re&lt;span style="color:#f92672"&gt;.&lt;/span&gt;compile(&lt;span style="color:#e6db74"&gt;r&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&amp;#39;(?P&amp;lt;quote&amp;gt;[&amp;#34;&amp;#39;])(?P&amp;lt;path&amp;gt;/blog/[^&amp;#34;&amp;#39;?#]+)(?P&amp;lt;tail&amp;gt;[?#][^&amp;#34;&amp;#39;]*)?(?P=quote)&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;add_trailing_slash&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;match&lt;/span&gt;: re&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Match[str]) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; str:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;group(&lt;span style="color:#e6db74"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; slug &lt;span style="color:#f92672"&gt;=&lt;/span&gt; path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rstrip(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rsplit(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)[&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; slug &lt;span style="color:#f92672"&gt;in&lt;/span&gt; post_slugs &lt;span style="color:#f92672"&gt;and&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;endswith(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;match&lt;span style="color:#f92672"&gt;.&lt;/span&gt;group(&lt;span style="color:#e6db74"&gt;&amp;#34;quote&amp;#34;&lt;/span&gt;)&lt;span style="color:#e6db74"&gt;}{&lt;/span&gt;path&lt;span style="color:#e6db74"&gt;}{&lt;/span&gt;match&lt;span style="color:#f92672"&gt;.&lt;/span&gt;group(&lt;span style="color:#e6db74"&gt;&amp;#34;tail&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;or&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}{&lt;/span&gt;match&lt;span style="color:#f92672"&gt;.&lt;/span&gt;group(&lt;span style="color:#e6db74"&gt;&amp;#34;quote&amp;#34;&lt;/span&gt;)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; link&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sub(add_trailing_slash, body)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;promote_multiline_code&lt;/span&gt;(body: str) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; str:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; soup &lt;span style="color:#f92672"&gt;=&lt;/span&gt; BeautifulSoup(body, &lt;span style="color:#e6db74"&gt;&amp;#34;html.parser&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; code &lt;span style="color:#f92672"&gt;in&lt;/span&gt; list(soup&lt;span style="color:#f92672"&gt;.&lt;/span&gt;find_all(&lt;span style="color:#e6db74"&gt;&amp;#34;code&amp;#34;&lt;/span&gt;)):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; code&lt;span style="color:#f92672"&gt;.&lt;/span&gt;find(&lt;span style="color:#e6db74"&gt;&amp;#34;br&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; code_text &lt;span style="color:#f92672"&gt;=&lt;/span&gt; dedent(code&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_text(separator&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, strip&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;))&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; len([line &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; line &lt;span style="color:#f92672"&gt;in&lt;/span&gt; code_text&lt;span style="color:#f92672"&gt;.&lt;/span&gt;splitlines() &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; line&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strip()]) &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pre &lt;span style="color:#f92672"&gt;=&lt;/span&gt; soup&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new_tag(&lt;span style="color:#e6db74"&gt;&amp;#34;pre&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; block &lt;span style="color:#f92672"&gt;=&lt;/span&gt; soup&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new_tag(&lt;span style="color:#e6db74"&gt;&amp;#34;code&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; block&lt;span style="color:#f92672"&gt;.&lt;/span&gt;string &lt;span style="color:#f92672"&gt;=&lt;/span&gt; code_text
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pre&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(block)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; code&lt;span style="color:#f92672"&gt;.&lt;/span&gt;replace_with(pre)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; str(soup)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;convert_html_to_markdown&lt;/span&gt;(body: str, post_slugs: set[str]) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; str:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prepared &lt;span style="color:#f92672"&gt;=&lt;/span&gt; promote_multiline_code(body)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; normalized &lt;span style="color:#f92672"&gt;=&lt;/span&gt; normalize_blog_links(normalize_html(prepared), post_slugs)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; markdownify(normalized, heading_style&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ATX&amp;#34;&lt;/span&gt;, bullets&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;-&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;date_value&lt;/span&gt;(value: str &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; str &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; value:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; parsed &lt;span style="color:#f92672"&gt;=&lt;/span&gt; datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fromisoformat(value&lt;span style="color:#f92672"&gt;.&lt;/span&gt;replace(&lt;span style="color:#e6db74"&gt;&amp;#34;Z&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;+00:00&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; parsed&lt;span style="color:#f92672"&gt;.&lt;/span&gt;isoformat()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;copy_media&lt;/span&gt;() &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; source, target &lt;span style="color:#f92672"&gt;in&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (SOURCE_MEDIA &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;img&amp;#34;&lt;/span&gt;, SITE &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;static/img&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (SOURCE_MEDIA &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;uploads&amp;#34;&lt;/span&gt;, SITE &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;static/media/uploads&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; source&lt;span style="color:#f92672"&gt;.&lt;/span&gt;is_dir():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; target&lt;span style="color:#f92672"&gt;.&lt;/span&gt;is_dir():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Tracked copies are sufficient when ignored source media is absent.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FileNotFoundError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Expected source or copied media directory: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;source&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; item &lt;span style="color:#f92672"&gt;in&lt;/span&gt; source&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rglob(&lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; item&lt;span style="color:#f92672"&gt;.&lt;/span&gt;is_file():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; relative &lt;span style="color:#f92672"&gt;=&lt;/span&gt; item&lt;span style="color:#f92672"&gt;.&lt;/span&gt;relative_to(source)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; destination &lt;span style="color:#f92672"&gt;=&lt;/span&gt; target &lt;span style="color:#f92672"&gt;/&lt;/span&gt; relative
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; destination&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parent&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mkdir(parents&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, exist_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; shutil&lt;span style="color:#f92672"&gt;.&lt;/span&gt;copy2(item, destination)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; required &lt;span style="color:#f92672"&gt;in&lt;/span&gt; (SITE &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;static/img/lsbw.svg&amp;#34;&lt;/span&gt;, SITE &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;static/img/favicon.ico&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; required&lt;span style="color:#f92672"&gt;.&lt;/span&gt;is_file():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FileNotFoundError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Missing required site asset: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;required&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;load_records&lt;/span&gt;() &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; list[dict[str, Any]]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; FIXTURE&lt;span style="color:#f92672"&gt;.&lt;/span&gt;open(encoding&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; source:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rows &lt;span style="color:#f92672"&gt;=&lt;/span&gt; json&lt;span style="color:#f92672"&gt;.&lt;/span&gt;load(source)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; isinstance(rows, list):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ValueError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Expected a Django fixture list in &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;FIXTURE&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; rows
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;convert&lt;/span&gt;() &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rows &lt;span style="color:#f92672"&gt;=&lt;/span&gt; load_records()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; copy_media()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grouped: dict[str, list[dict[str, Any]]] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; defaultdict(list)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; rows:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grouped[row[&lt;span style="color:#e6db74"&gt;&amp;#34;model&amp;#34;&lt;/span&gt;]]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; categories &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; row[&lt;span style="color:#e6db74"&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;]: row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;] &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; grouped[&lt;span style="color:#e6db74"&gt;&amp;#34;blog.blogcategory&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; keywords &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; row[&lt;span style="color:#e6db74"&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;]: row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;] &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; grouped[&lt;span style="color:#e6db74"&gt;&amp;#34;generic.keyword&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; category_by_slug &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {value[&lt;span style="color:#e6db74"&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;]: value &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; value &lt;span style="color:#f92672"&gt;in&lt;/span&gt; categories&lt;span style="color:#f92672"&gt;.&lt;/span&gt;values()}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tag_title_by_slug &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {value[&lt;span style="color:#e6db74"&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;]: value[&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;] &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; value &lt;span style="color:#f92672"&gt;in&lt;/span&gt; keywords&lt;span style="color:#f92672"&gt;.&lt;/span&gt;values()}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; category &lt;span style="color:#f92672"&gt;in&lt;/span&gt; categories&lt;span style="color:#f92672"&gt;.&lt;/span&gt;values():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tag_title_by_slug&lt;span style="color:#f92672"&gt;.&lt;/span&gt;setdefault(category[&lt;span style="color:#e6db74"&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;], category[&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; keyword_by_post: dict[str, list[str]] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; defaultdict(list)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; grouped[&lt;span style="color:#e6db74"&gt;&amp;#34;generic.assignedkeyword&amp;#34;&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fields &lt;span style="color:#f92672"&gt;=&lt;/span&gt; row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content_type &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;content_type&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; content_type &lt;span style="color:#f92672"&gt;==&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;blog&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;blogpost&amp;#34;&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; keyword &lt;span style="color:#f92672"&gt;=&lt;/span&gt; keywords&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;keyword&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; keyword:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; keyword_by_post[str(fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;object_pk&amp;#34;&lt;/span&gt;))]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(keyword[&lt;span style="color:#e6db74"&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post_rows &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sorted(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (row &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; grouped[&lt;span style="color:#e6db74"&gt;&amp;#34;blog.blogpost&amp;#34;&lt;/span&gt;] &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; PUBLISHED_STATUS),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;lambda&lt;/span&gt; row: row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;publish_date&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;or&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reverse&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; posts_by_pk &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {str(row[&lt;span style="color:#e6db74"&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;]): row &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; post_rows}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; published_slugs &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;][&lt;span style="color:#e6db74"&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;] &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; post_rows}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post_slugs: set[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; set()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; archive_months: set[tuple[int, int]] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; set()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blog&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mkdir(parents&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, exist_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; write_content(CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_index.md&amp;#34;&lt;/span&gt;, {&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Blog&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;home&amp;#34;&lt;/span&gt;}, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; write_content(CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blog&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_index.md&amp;#34;&lt;/span&gt;, {&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Blog&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;blog&amp;#34;&lt;/span&gt;}, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; post_rows:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fields &lt;span style="color:#f92672"&gt;=&lt;/span&gt; row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; slug &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; re&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fullmatch(&lt;span style="color:#e6db74"&gt;r&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;[a-z0-9]+(?:-[a-z0-9]+)*&amp;#34;&lt;/span&gt;, slug):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ValueError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Unexpected blog slug in fixture: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;slug&lt;span style="color:#e6db74"&gt;!r}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; slug &lt;span style="color:#f92672"&gt;in&lt;/span&gt; post_slugs:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ValueError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Duplicate blog slug in fixture: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;slug&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post_slugs&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add(slug)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post_tag_slugs: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; category_pk &lt;span style="color:#f92672"&gt;in&lt;/span&gt; fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;categories&amp;#34;&lt;/span&gt;, []):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; category &lt;span style="color:#f92672"&gt;=&lt;/span&gt; categories&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(category_pk)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; category:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ValueError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Missing blog category &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;category_pk&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; for post &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;slug&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post_tag_slugs&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(category[&lt;span style="color:#e6db74"&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post_tag_slugs&lt;span style="color:#f92672"&gt;.&lt;/span&gt;extend(keyword_by_post&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(str(row[&lt;span style="color:#e6db74"&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;]), []))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post_tag_slugs &lt;span style="color:#f92672"&gt;=&lt;/span&gt; list(dict&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fromkeys(post_tag_slugs))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; tag_slug &lt;span style="color:#f92672"&gt;in&lt;/span&gt; post_tag_slugs:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; tag_slug &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; tag_title_by_slug:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ValueError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Missing tag term &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;tag_slug&lt;span style="color:#e6db74"&gt;!r}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; for post &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;slug&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tags &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [tag_title_by_slug[tag_slug] &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; tag_slug &lt;span style="color:#f92672"&gt;in&lt;/span&gt; post_tag_slugs]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; related_slugs &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; related_pk &lt;span style="color:#f92672"&gt;in&lt;/span&gt; fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;related_posts&amp;#34;&lt;/span&gt;, []):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; related &lt;span style="color:#f92672"&gt;=&lt;/span&gt; posts_by_pk&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(str(related_pk))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; related:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; related_slugs&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(related[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;][&lt;span style="color:#e6db74"&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; published &lt;span style="color:#f92672"&gt;=&lt;/span&gt; date_value(fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;publish_date&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; published:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ValueError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Published post &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;slug&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; has no publish date&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; published_date &lt;span style="color:#f92672"&gt;=&lt;/span&gt; datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fromisoformat(published)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; archive_months&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add((published_date&lt;span style="color:#f92672"&gt;.&lt;/span&gt;year, published_date&lt;span style="color:#f92672"&gt;.&lt;/span&gt;month))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; front_matter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: fields[&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;: slug,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;date&amp;#34;&lt;/span&gt;: published,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;lastmod&amp;#34;&lt;/span&gt;: date_value(fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;updated&amp;#34;&lt;/span&gt;)),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;draft&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;blog&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;: tags,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;: [AUTHOR_NAME],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;related_posts&amp;#34;&lt;/span&gt;: related_slugs,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; write_content(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blog&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;slug&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.md&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; front_matter,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; convert_html_to_markdown(fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;), published_slugs),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blog&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;slug&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.html&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unlink(missing_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Categories become tags; remove generated category pages and redirect their old routes.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; category_slugs &lt;span style="color:#f92672"&gt;=&lt;/span&gt; set(category_by_slug)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; slug &lt;span style="color:#f92672"&gt;in&lt;/span&gt; category_slugs:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blog/category&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; slug &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_index.md&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unlink(missing_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Hugo taxonomies generate tag term pages dynamically; remove old imported term files.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; slug &lt;span style="color:#f92672"&gt;in&lt;/span&gt; tag_title_by_slug:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blog/tag&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; slug &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_index.md&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unlink(missing_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blog/author/lss&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_index.md&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unlink(missing_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; year, month &lt;span style="color:#f92672"&gt;in&lt;/span&gt; archive_months:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blog/archive&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; str(year) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; str(month) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_index.md&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unlink(missing_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; write_content(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blog/archive&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_index.md&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Archive&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;archive-index&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;draft&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Convert the Mezzanine page tree. The dynamic contact form is intentionally&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# omitted; keep its route as a landing page for the static contact details.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; richtext &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {row[&lt;span style="color:#e6db74"&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;]: row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;] &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; grouped[&lt;span style="color:#e6db74"&gt;&amp;#34;pages.richtextpage&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gallery &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {row[&lt;span style="color:#e6db74"&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;]: row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;] &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; grouped[&lt;span style="color:#e6db74"&gt;&amp;#34;galleries.gallery&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gallery_images &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sorted(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grouped[&lt;span style="color:#e6db74"&gt;&amp;#34;galleries.galleryimage&amp;#34;&lt;/span&gt;], key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;lambda&lt;/span&gt; row: row&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; grouped[&lt;span style="color:#e6db74"&gt;&amp;#34;pages.page&amp;#34;&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fields &lt;span style="color:#f92672"&gt;=&lt;/span&gt; row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; PUBLISHED_STATUS:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; slug &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fields[&lt;span style="color:#e6db74"&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strip(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; slug &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;See [About](/contact/about/) for profile and contact links, or &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;[Legal info](/contact/legal/) for the site&amp;#39;s legal details.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path &lt;span style="color:#f92672"&gt;=&lt;/span&gt; CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;contact&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;_index.md&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;elif&lt;/span&gt; fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;content_model&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;gallery&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gallery_fields &lt;span style="color:#f92672"&gt;=&lt;/span&gt; gallery&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(row[&lt;span style="color:#e6db74"&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;], {})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body &lt;span style="color:#f92672"&gt;=&lt;/span&gt; convert_html_to_markdown(gallery_fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;), published_slugs)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{{&amp;lt; gallery &amp;gt;}}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gallery_items &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; image_row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; gallery_images:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image &lt;span style="color:#f92672"&gt;=&lt;/span&gt; image_row[&lt;span style="color:#e6db74"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; image&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;gallery&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; row[&lt;span style="color:#e6db74"&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; relative &lt;span style="color:#f92672"&gt;=&lt;/span&gt; PurePosixPath(image[&lt;span style="color:#e6db74"&gt;&amp;#34;file&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; relative&lt;span style="color:#f92672"&gt;.&lt;/span&gt;is_absolute() &lt;span style="color:#f92672"&gt;or&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;..&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; relative&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parts:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ValueError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Unsafe gallery asset path: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;image[&lt;span style="color:#e6db74"&gt;&amp;#39;file&amp;#39;&lt;/span&gt;]&lt;span style="color:#e6db74"&gt;!r}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; asset &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SITE &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;static/media&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; relative
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; asset&lt;span style="color:#f92672"&gt;.&lt;/span&gt;is_file():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FileNotFoundError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Missing gallery image: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;asset&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/media/&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; relative&lt;span style="color:#f92672"&gt;.&lt;/span&gt;as_posix()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gallery_items&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append({&lt;span style="color:#e6db74"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: url, &lt;span style="color:#e6db74"&gt;&amp;#34;caption&amp;#34;&lt;/span&gt;: image&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;)})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gallery_data_path &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SITE &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;data/gallery.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gallery_data_path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parent&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mkdir(parents&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, exist_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gallery_data_path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;write_text(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; json&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dumps({&lt;span style="color:#e6db74"&gt;&amp;#34;images&amp;#34;&lt;/span&gt;: gallery_items}, ensure_ascii&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;, indent&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; encoding&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path &lt;span style="color:#f92672"&gt;=&lt;/span&gt; CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;slug&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.md&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; page_content &lt;span style="color:#f92672"&gt;=&lt;/span&gt; richtext&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(row[&lt;span style="color:#e6db74"&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;], {})&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body &lt;span style="color:#f92672"&gt;=&lt;/span&gt; convert_html_to_markdown(page_content, published_slugs)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path &lt;span style="color:#f92672"&gt;=&lt;/span&gt; CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;slug&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.md&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; slug &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (CONTENT &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;slug&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.html&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unlink(missing_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; page_fields &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: fields[&lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;gallery&amp;#34;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;content_model&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;gallery&amp;#34;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;pages&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;date&amp;#34;&lt;/span&gt;: date_value(fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;publish_date&amp;#34;&lt;/span&gt;)),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Contact information and legal details.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; slug &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;draft&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; write_content(path, page_fields, body)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data_dir &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SITE &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data_dir&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mkdir(parents&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, exist_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (data_dir &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;sidebar.json&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unlink(missing_ok&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Converted &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;len(post_rows)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; published posts, &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;sum(fields&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; PUBLISHED_STATUS &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; fields &lt;span style="color:#f92672"&gt;in&lt;/span&gt; (r[&lt;span style="color:#e6db74"&gt;&amp;#39;fields&amp;#39;&lt;/span&gt;] &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; r &lt;span style="color:#f92672"&gt;in&lt;/span&gt; grouped[&lt;span style="color:#e6db74"&gt;&amp;#39;pages.page&amp;#39;&lt;/span&gt;]))&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; pages, &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;and &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;sum(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; r &lt;span style="color:#f92672"&gt;in&lt;/span&gt; gallery_images)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; gallery images into &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;SITE&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; __name__ &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; convert()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>How to counter vCard address book "bit rot"</title><link>https://blog.lsweb.eu/blog/how-to-counter-vcard-address-book-bit-rot/</link><pubDate>Fri, 30 Oct 2026 10:25:00 +0000</pubDate><guid>https://blog.lsweb.eu/blog/how-to-counter-vcard-address-book-bit-rot/</guid><description>&lt;p&gt;After a few years and mobile device generations the state of my address book was rather frustrating:
Starting with Nokia Symbian S60 (&lt;a href="https://sourceforge.net/projects/funambol" target="_blank" rel="noopener noreferrer"&gt;SyncML&lt;/a&gt;) over to early Android (using &lt;a href="https://www.fjsoft.at/de" target="_blank" rel="noopener noreferrer"&gt;Backups&lt;/a&gt;), on to BlackBerry 10 (Card/CalDAV &lt;a href="https://radicale.org" target="_blank" rel="noopener noreferrer"&gt;backend&lt;/a&gt;) and ultimately back to Android (using a &lt;a href="https://www.davx5.com" target="_blank" rel="noopener noreferrer"&gt;sync&lt;/a&gt; app) has introduced numerous duplications and invisible character additions by the respective address book apps (the Samsung &lt;a href="https://www.samsung.com/uk/support/mobile-devices/how-do-i-manage-my-contacts" target="_blank" rel="noopener noreferrer"&gt;Contacts&lt;/a&gt; app being the worst).&lt;/p&gt;
&lt;p&gt;In the era of agentic engineering that seemed like a good task for the &lt;a href="https://github.com/glennvdv/opencode-dockerized" target="_blank" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt; agent harness (in combination with &lt;a href="https://github.com/Niko1221/Strata" target="_blank" rel="noopener noreferrer"&gt;Strata&lt;/a&gt; as local LLM provider) to create a fix. A few iterations and testing with over 1000 contact entries yielded very promising results - so here is the &lt;a href="https://github.com/BOFH2k/vcf-cleaner" target="_blank" rel="noopener noreferrer"&gt;repo&lt;/a&gt; for anyone who might find the resulting Python script useful as well: &lt;code&gt;https://github.com/BOFH2k/vcf-cleaner&lt;/code&gt;&lt;/p&gt;</description></item><item><title>How to compare vCard address book entries</title><link>https://blog.lsweb.eu/blog/how-to-compare-vcard-address-book-entries/</link><pubDate>Fri, 06 Apr 2018 10:46:40 +0000</pubDate><guid>https://blog.lsweb.eu/blog/how-to-compare-vcard-address-book-entries/</guid><description>&lt;p&gt;Following a little mishap with the &lt;a href="https://f-droid.org/en/packages/at.bitfire.davdroid" target="_blank" rel="noopener noreferrer"&gt;DAVdroid&lt;/a&gt; app on my Fire HDX Android tablet, I ended up with a lot of duplicate contacts in my CardDAV account. Upon closer inspection I then noticed, that in the process of copying the contacts from the local contact store (on my Blackberry smartphone) to the CardDAV account, certain contact details had been lost and spurious characters (like spaces and question marks) introduced.&lt;/p&gt;
&lt;p&gt;Additionally, I had started editing the contact details for proper display in the Android contact app by Blackberry: The app does unfortunately not display the organization info provided for contacts not connected to a person (like institutions) in the address book view - contrary to that view on BlackBerry 10 before.&lt;/p&gt;</description></item><item><title>How to get Mezzanine up and running on cPanel shared hosting</title><link>https://blog.lsweb.eu/blog/how-to-get-mezzanine-up-and-running-on-cpanel-shared-hosting/</link><pubDate>Tue, 13 Feb 2018 19:15:05 +0000</pubDate><guid>https://blog.lsweb.eu/blog/how-to-get-mezzanine-up-and-running-on-cpanel-shared-hosting/</guid><description>&lt;p&gt;The follwing articles were of much use when switching this weblog from the no longer maintained &lt;a href="http://nucleuscms.org" target="_blank" rel="noopener noreferrer" title="Nucleus"&gt;Nucleus&lt;/a&gt; to &lt;a href="http://mezzanine.jupo.org" target="_blank" rel="noopener noreferrer" title="Mezzanine"&gt;Mezzanine&lt;/a&gt;, a modern blog and content management system, based on the &lt;a href="https://www.djangoproject.com" target="_blank" rel="noopener noreferrer" title="Django"&gt;Django&lt;/a&gt; web application framework and Python:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setting up a Python virtual environment (required to be able to install local packages via &lt;code&gt;pip&lt;/code&gt;, the Python package installer):&lt;br&gt;
&lt;a href="https://web.archive.org/web/20180314215305/http://janealam.com/2017/10/06/how-to-install-virtualenv-python-for-flask-app-on-cpanel-base-shared-hosting" target="_blank" rel="noopener noreferrer" title="VEnv"&gt;http://janealam.com/2017/10/06/how-to-install-virtualenv-python-for-flask-app-on-cpanel-base-shared-hosting&lt;/a&gt; (archived)&lt;/li&gt;
&lt;li&gt;Installing Mezzanine with its dependencies (e.g. &lt;code&gt;psycopg2&lt;/code&gt; python support for the PostgreSQL database to be used as content repository):&lt;br&gt;
&lt;a href="https://www.rosslaird.com/2013/06/10/first-steps-with-mezzanine" target="_blank" rel="noopener noreferrer"&gt;https://www.rosslaird.com/2013/06/10/first-steps-with-mezzanine&lt;/a&gt; (no longer available)&lt;/li&gt;
&lt;li&gt;Running Mezzanine through the Phusion Passenger &lt;a href="https://documentation.cpanel.net/display/66Docs/66&amp;#43;Release&amp;#43;Notes#id-66ReleaseNotes-ApplicationManagerinterface" target="_blank" rel="noopener noreferrer" title="cPanel 66"&gt;Application Manager&lt;/a&gt; available in cPanel (via its &lt;a href="http://www.python.org/dev/peps/pep-3333" target="_blank" rel="noopener noreferrer" title="WSGI"&gt;WSGI&lt;/a&gt; loader):&lt;br&gt;
&lt;a href="https://help.dreamhost.com/hc/en-us/articles/215769548-Passenger-and-Python-WSGI" target="_blank" rel="noopener noreferrer" title="Passenger"&gt;https://help.dreamhost.com/hc/en-us/articles/215769548-Passenger-and-Python-WSGI&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Manual restore of Android databases fails</title><link>https://blog.lsweb.eu/blog/manual-restore-of-android-databases-fails/</link><pubDate>Fri, 04 Nov 2011 16:11:37 +0000</pubDate><guid>https://blog.lsweb.eu/blog/manual-restore-of-android-databases-fails/</guid><description>&lt;p&gt;As described in my earlier &lt;a href="https://blog.lsweb.eu/blog/android-firmware-updates-for-the-lg-gw620/"&gt;post&lt;/a&gt;, it is wise to create regular backups of your apps and data to SD card.&lt;/p&gt;
&lt;p&gt;This can be accomplished using &lt;code&gt;adb shell&lt;/code&gt; or the local Android terminal:&lt;br&gt;
&lt;code&gt;busybox tar cvf /mnt/sdcard/Backup/Full_`busybox date +%Y%m%d`.tar /system /data&lt;/code&gt;&lt;br&gt;
The correspondent command to for example restore your text messages would be:&lt;br&gt;
&lt;code&gt;cd /;busybox tar xvf /mnt/sdcard/Backup/Full_20111101.tar data/data/com.android.providers.telephony/databases/mmssms.db&lt;/code&gt;&lt;br&gt;
This worked fine up to Android 2.2, provided that you check file ownership and permissions afterwards, e.g.:&lt;/p&gt;</description></item><item><title>Android firmware updates for the LG GW620</title><link>https://blog.lsweb.eu/blog/android-firmware-updates-for-the-lg-gw620/</link><pubDate>Tue, 21 Jun 2011 14:36:00 +0000</pubDate><guid>https://blog.lsweb.eu/blog/android-firmware-updates-for-the-lg-gw620/</guid><description>&lt;p&gt;The OpenEtna community provides firmware updates for the GW620 phone by LG, which originally came with Android 1.5.&lt;/p&gt;
&lt;p&gt;After using the Korean Android 2.2 (Froyo) update provided by LG for a while, I then took the leap to OpenEVE Android 2.3 (Gingerbread) for usability reasons&lt;br&gt;
(2.2 was pretty laggy sometimes).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make a backup of your apps and data to SD card (this assumes you start off a rooted Froyo firmware with &lt;code&gt;busybox&lt;/code&gt; installed) using &lt;code&gt;adb shell&lt;/code&gt; or the local Android terminal:&lt;br&gt;
&lt;code&gt;busybox tar cvf /mnt/sdcard/Backup/Full_20110621.tar /system /data&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Reboot your device in fastboot mode (please note that mode might not be available, if you have upgraded to Froyo V20 using the offical LG KDZ updater for WIndows): Remove battery, then hook up USB cable while pressing &lt;code&gt;Menu+Space&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Backup your old boot and system images using fastboot and a recovery image: &lt;code&gt;fastboot boot gb-recovery.img&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Reset the device settings: &lt;code&gt;fastboot -w&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Flash the boot image: &lt;code&gt;fastboot flash boot boot234.img&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Flash the system image: &lt;code&gt;fastboot flash system system234.img&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Apply ZIP patches from SD card using fastboot to ClockWork Mod recovery image: &lt;code&gt;fastboot boot gb-recovery.img&lt;/code&gt;&lt;br&gt;
Apply gapps-gb-20110307-signed.zip &amp;amp; boot-gb234-110509-rndis-224mb.zip&lt;/li&gt;
&lt;li&gt;Untar your backup from SD card locally and push back data, as required, e.g.:
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;adb push contacts2.db /data/data/com.android.providers.contacts/databases 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;adb push calendar.db /data/data/com.android.providers.calendar/databases 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;adb push mmssms.db /data/data/com.android.providers.telephony/databases 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;adb push note_pad.db /data/data/org.openintents.notepad/databases 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;adb push shopping.db /data/data/org.openintents.shopping/databases
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;adb push mytracks.db /data/data/com.google.android.maps.mytracks/databases 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;adb push safe /data/data/org.openintents.safe/databases 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;adb push database /data/data/com.timsu.astrid/databases
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&amp;hellip;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Depending on the image used, you might have to change the permissions for the files to be able to overwrite them: &lt;code&gt;chmod 666 ...&lt;/code&gt; (and back to &lt;code&gt;chmod 660 ...&lt;/code&gt; after the push). Also check that the uid/gid combination still agrees with the app directory and adjust using &lt;code&gt;busybox chown app_xy:app_xy ...&lt;/code&gt; (unless you are doing a device shell restore via &lt;code&gt;busybox tar&lt;/code&gt;, but even then the uid/gid combination might have changed for the new installation)!&lt;br&gt;
For more details please refer to the &lt;a href="https://web.archive.org/web/20120421193224/http://forum.openetna.com:80/index.php?board=1.0" target="_blank" rel="noopener noreferrer"&gt;OpenEtna&lt;/a&gt; forum.&lt;br&gt;
Infos on manual backups of Android databases may be found &lt;a href="https://web.archive.org/web/20120226083844/http://wiki.dandascalescu.com/howtos/android#How_to_backup_Android_SMSes.2C_MMSes.2C_Contacts.2C_Call_Logs.2C_User_dictionary_etc._manually" target="_blank" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>How to dual-boot Windows physically and virtually</title><link>https://blog.lsweb.eu/blog/how-to-dual-boot-windows-physically-and-virtually/</link><pubDate>Tue, 30 Nov 2010 09:22:55 +0000</pubDate><guid>https://blog.lsweb.eu/blog/how-to-dual-boot-windows-physically-and-virtually/</guid><description>&lt;p&gt;While this is no longer possible with Windows Activation Technologies in Win 7, Windows XP allows using Hardware Profiles for a shared installation that can be run either physically or virtually (e.g. using the KVM virtualization stack built into recent Linux kernels, which is a lot more convenient than VMWare Player with its kernel module dependencies, at the price of 3D graphics acceleration).&lt;/p&gt;
&lt;p&gt;The recommended sequence for such a setup:&lt;/p&gt;</description></item><item><title>How to repair an ext3 filesystem after mkdosfs format</title><link>https://blog.lsweb.eu/blog/how-to-repair-an-ext3-filesystem-after-mkdosfs-format/</link><pubDate>Thu, 05 Feb 2009 20:42:44 +0000</pubDate><guid>https://blog.lsweb.eu/blog/how-to-repair-an-ext3-filesystem-after-mkdosfs-format/</guid><description>&lt;p&gt;After shrinking a large &lt;a href="http://en.wikipedia.org/wiki/Ext3" target="_blank" rel="noopener noreferrer"&gt;ext3&lt;/a&gt; partition using gparted to make space for an additional partition, I did not take sufficient care to notice the leading partition I newly created with &lt;code&gt;cfdisk&lt;/code&gt; subsequently was denoted as &lt;code&gt;sdc2&lt;/code&gt; instead of &lt;code&gt;sdc1&lt;/code&gt; (as one might expect).&lt;/p&gt;
&lt;p&gt;Thus woe ensued when I issued a &lt;code&gt;mkdosfs -F 32 /dev/sdc1&lt;/code&gt; command without a closer look at the new partition numbering.&lt;/p&gt;
&lt;p&gt;So the interesting question now was, what can be salvaged from an ext3 filesystem with overwritten superblock (as well as a few of the inodes)? Luckily, the erroneous reformatting was FAT32, not ext3, which would have caused the inode block pointers to be zeroed, to quote the ext3 &lt;a href="http://batleth.sapienti-sat.org/projects/FAQs/ext3-faq.html" target="_blank" rel="noopener noreferrer"&gt;FAQ&lt;/a&gt;:&lt;br&gt;
&amp;ldquo;In order to ensure that ext3 can safely resume an unlink after a crash, it actually zeros out the block pointers in the inode, whereas ext2 just marks these blocks as unused in the block bitmaps and marks the inode as &amp;ldquo;deleted&amp;rdquo; and leaves the block pointers alone.&amp;rdquo;&lt;/p&gt;</description></item><item><title>How to boot a real harddisk in VMWare</title><link>https://blog.lsweb.eu/blog/how-to-boot-a-real-harddisk-in-vmware/</link><pubDate>Mon, 08 Jan 2007 19:53:00 +0000</pubDate><guid>https://blog.lsweb.eu/blog/how-to-boot-a-real-harddisk-in-vmware/</guid><description>&lt;p&gt;The knack is to install the VMWare SCSI driver (included with VMWare Tools) before booting the SATA or PATA harddisk.&lt;/p&gt;
&lt;p&gt;Then configure it in the VMX config file as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scsi0:0.present = &amp;#34;TRUE&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scsi0:0.fileName = &amp;#34;win_raw.vmdk&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scsi0:0.deviceType = &amp;#34;rawDisk&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scsi0.present = &amp;#34;TRUE&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scsi0.virtualDev = &amp;#34;buslogic&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s the contents of &lt;code&gt;win_raw.vmdk&lt;/code&gt; for my harddrive:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Disk DescriptorFile
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;version=1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CID=adf9ef51
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;parentCID=ffffffff
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;createType=&amp;#34;fullDevice&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Extent description
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RW 625142448 FLAT &amp;#34;/dev/sda&amp;#34; 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# The Disk Data Base
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;#DDB
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddb.virtualHWVersion = &amp;#34;3&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddb.geometry.cylinders = &amp;#34;38913&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddb.geometry.heads = &amp;#34;255&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddb.geometry.sectors = &amp;#34;63&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddb.geometry.biosCylinders = &amp;#34;38913&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddb.geometry.biosHeads = &amp;#34;255&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddb.geometry.biosSectors = &amp;#34;63&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddb.adapterType = &amp;#34;buslogic&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddb.toolsVersion = &amp;#34;6404&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And how to get those parameters? Exents und geometry can for instance be deduced from &lt;code&gt;hdparm /dev/sda&lt;/code&gt;:&lt;/p&gt;</description></item></channel></rss>