Matthias Kestenholz: Posts about Weeknoteshttps://406.ch/writing/category-weeknotes/2025-04-09T12:00:00ZMatthias KestenholzWeeknotes (2025 week 15)https://406.ch/writing/weeknotes-2025-week-15/2025-04-09T12:00:00Z2025-04-09T12:00:00Z<h1 id="weeknotes-2025-week-15"><a class="toclink" href="#weeknotes-2025-week-15">Weeknotes (2025 week 15)</a></h1> <h2 id="djangonaut-space"><a class="toclink" href="#djangonaut-space">Djangonaut Space</a></h2> <p>We have already reached the final week of the <a href="https://djangonaut.space/">Djangonaut Space</a> session 4. I had a great time as a navigator and am looking forward to participate more, but for now I&rsquo;m also glad that I do not have the additional responsibility at least for the close future.</p> <p>We have done great work on the <a href="https://pypi.org/project/django-debug-toolbar/">django-debug-toolbar</a> in our group, more is to come.</p> <h2 id="progress-on-the-prose-editor"><a class="toclink" href="#progress-on-the-prose-editor">Progress on the prose editor</a></h2> <p>I have done much work on <a href="https://pypi.org/project/django-prose-editor/">django-prose-editor</a> in the last few weeks and after a large list of alphas and betas I&rsquo;m nearing a state which I want to release into the wild.</p> <p>The integration has been completely rethought (again) and now uses JavaScript modules and importmaps. The ground work to support all of that in Django has been laid in <a href="https://pypi.org/project/django-js-asset/">django-js-asset</a>.</p> <p>The nice thing about using JavaScript modules and importmaps is that we now have an easy way to combine the power of modern JavaScript customization with easy cache busting using Django&rsquo;s <code>ManifestStaticFilesStorage</code>. A longer post on this is brewing and I hope to have it ready soon-ish.</p> <p>As a sneak peek, here&rsquo;s the way it works:</p> <div class="chl"><pre><span></span><code><span class="kn">from</span> <span class="nn">django_prose_editor.fields</span> <span class="kn">import</span> <span class="n">ProseEditorField</span> <span class="n">content</span> <span class="o">=</span> <span class="n">ProseEditorField</span><span class="p">(</span> <span class="n">extensions</span><span class="o">=</span><span class="p">{</span> <span class="s2">&quot;Bold&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s2">&quot;Italic&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s2">&quot;BulletList&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s2">&quot;Link&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="p">},</span> <span class="c1"># sanitize=True is the default when using extensions</span> <span class="p">)</span> </code></pre></div> <p>The nice thing about it is that the sanitization allowlist for <a href="https://github.com/messense/nh3">nh3</a> only includes tags and attributes which are enabled via the <code>extensions</code> dict. So, you don&rsquo;t have to do anything else to be safe from XSS etc.</p> <p>Check out the pre-releases on <a href="https://pypi.org/project/django-prose-editor/#history">PyPI</a> or have a look at the <a href="https://django-prose-editor.readthedocs.io/">documentation</a> to learn more about this project!</p> <h2 id="using-claude-code"><a class="toclink" href="#using-claude-code">Using Claude Code</a></h2> <p>I have been using Claude Code (without editor integrations, thank you very much) more and more. It&rsquo;s a good coding companion when it comes to throwing around ideas, drafting docs and writing unit tests including integration tests.</p> <p>Sometimes I&rsquo;m really surprised at how good it is. Other times&hellip; less so. The tool often finds a way to get tests passing, but when the editor integration tests directly manipulate <code>innerHTML</code> and then Claude proclaims that interacting with the editor is now shown to work I have to chuckle a bit. And when I insist on doing what I mean and not just finding broken workarounds it doesn&rsquo;t really change anything. After spinning more we&rsquo;re always back where we started.</p> <p>I am somewhat glad that this is where we&rsquo;re at now. I&rsquo;m not 100% sure if it&rsquo;s progress. At least it&rsquo;s surprisingly funny at times.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <p>I haven&rsquo;t written a regular weeknotes entry since the end of January, so naturally the list here is longer than usual.</p> <ul> <li><a href="https://pypi.org/project/feincms3-forms/">feincms3-forms 0.5.1</a>: I inadvertently bumped the Django dependency without actually wanting that; this patch release reverts that (while adding official support for new Django and Python versions).</li> <li><a href="https://pypi.org/project/django-mptt/">django-mptt 0.17</a>: Mariusz has done all the hard work for supporting newer versions of Django. I just had to press the release button. That being said, four years after marking the package as unmaintained I&rsquo;m still maintaining it. At least I don&rsquo;t get complaints anymore&hellip;</li> <li><a href="https://pypi.org/project/django-json-schema-editor/">django-json-schema-editor 0.4</a>: Added a dependency on the pre-release of django-prose-editor and added a test suite including integration tests so that we actually now when stuff breaks the next time!</li> <li><a href="https://pypi.org/project/django-debug-toolbar/">django-debug-toolbar 5.1</a>: See above.</li> <li><a href="https://pypi.org/project/feincms3-data/">feincms3-data</a>: Added fixes to dump distinct objects. Spent more time than useful on the Django change which added a final newline to JSON-serialized data.</li> <li><a href="https://pypi.org/project/django-js-asset/">django-js-asset 3.1.2</a>: Importmaps support, added a <code>static_lazy</code> helper which is useful to define module-scoped static URLs. The later wouldn&rsquo;t work with the <code>ManifestStaticFilesStorage</code> because the manifest doesn&rsquo;t yet exist when <code>collectstatic</code> runs, so the actual evaluation of static URLs has to be postponed. The lazy version solves this nicely.</li> <li><a href="https://pypi.org/project/feincms3-sites/">feincms3-sites 0.21.1</a> and <a href="https://pypi.org/project/feincms3-language-sites/">feincms3-language-sites 0.4.1</a>: See the relevant <a href="https://406.ch/writing/til-tools-exist-which-do-not-lowercase-domain-names-when-requesting-websites-over-http-s/">TIL</a> blogpost.</li> </ul>Weeknotes (2025 week 05)https://406.ch/writing/weeknotes-2025-week-05/2025-01-29T12:00:00Z2025-01-29T12:00:00Z<h1 id="weeknotes-2025-week-05"><a class="toclink" href="#weeknotes-2025-week-05">Weeknotes (2025 week 05)</a></h1> <h2 id="djangonaut-space"><a class="toclink" href="#djangonaut-space">Djangonaut Space</a></h2> <p>In December I wrote a few paragraphs about <a href="https://406.ch/writing/weeknotes-2024-week-49/">my decision to not run for the Django Steering Council</a>, mentioning that I want to contribute in different ways.</p> <p>I have offered to contribute to Djangonaut Space to do some mentoring. I&rsquo;m already a bit stressed, but that&rsquo;s normal and to be expected. I&rsquo;ll probably have more to share about that in the close future!</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://github.com/feincms/feincms3-cookiecontrol/commits/main/">feincms-cookiecontrol 1.6</a>: Removed the hardcoded dependency upon <a href="https://feincms3.readthedocs.io/">feincms3</a> and some additional code golfing. The cookie banner JavaScript is now back to &lt;4KiB.</li> <li><a href="https://pypi.org/project/django-curtains/">django-curtains 0.7</a>: Updated the CI job list and modernized the package somewhat, no code changes necessary. It&rsquo;s good to release updated versions though just to show that it&rsquo;s still actively maintained.</li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.10.3</a>: Small CSS fixes and mainly updated TipTap/ProseMirror.</li> <li><a href="https://pypi.org/project/django-imagefield/">django-imagefield 0.22</a>: The updated version no longer autodeletes processed images; this wasn&rsquo;t really a problem before but I was a little bit fearful that images are still referenced elsewhere and this change let&rsquo;s me sleep better.</li> <li><a href="https://pypi.org/project/feincms-oembed/">feincms-oembed 2.0</a>: Oembed support for FeinCMS 1 without actually depending upon the FeinCMS package itself. Still works.</li> <li><a href="https://pypi.org/project/django-content-editor/">django-content-editor 7.2</a>: The <code>Region</code> type is now hashable; this may be useful, or not.</li> <li><a href="https://pypi.org/project/feincms3/">feincms3 5.3.1</a>: I undeprecated the <code>TemplateMixin</code> because even though <code>PageTypeMixin</code> is nicer, sometimes all you need is a template selector.</li> </ul>Weeknotes (2025 week 03)https://406.ch/writing/weeknotes-2025-week-03/2025-01-15T12:00:00Z2025-01-15T12:00:00Z<h1 id="weeknotes-2025-week-03"><a class="toclink" href="#weeknotes-2025-week-03">Weeknotes (2025 week 03)</a></h1> <h2 id="claude-ai-helped-me-for-the-first-time"><a class="toclink" href="#claude-ai-helped-me-for-the-first-time">Claude AI helped me for the first time</a></h2> <p><a href="https://github.com/matthiask/django-imagefield">django-imagefield</a> prefers processing thumbnails, cropped images etc. directly when saving the model and not later on demand; it&rsquo;s faster and also you&rsquo;ll know it immediately when an image couldn&rsquo;t be processed for some reason instead of only later when people actually try browsing your site.</p> <p>A consequence is that if you change formats you have to remember that you have to reprocess the images. The Django app comes with a management command <code>./manage.py process_imagefields</code> to help with this. I have added parallel processing based on <code>concurrent.futures</code> to it some time ago so that the command completes faster when it is being run on a system with several cores.</p> <p>A work colleague is using macOS (many are, in fact), and he always got multiprocessing Python crashes. This is a well known issue and I remember reading about it a few years ago. I checked the docs and saw that the <a href="https://docs.python.org/3/library/concurrent.futures.html"><code>concurrent.futures</code></a> page doesn&rsquo;t mention macOS, but <a href="https://docs.python.org/3/library/multiprocessing.html"><code>multiprocessing</code></a> does. So, I hoped that a simple rewrite of the management command using <code>multiprocessing</code> might fix it.</p> <p>Because I was in a rush and really didn&rsquo;t want to do it I turned to an AI assistant for doing this boring work. To my surprise it immediately produced a version which I could easily fix by hand to produce a working version. Of course, the initial response was totally broken, removed code it wasn&rsquo;t supposed to, and even the syntax was invalid. I didn&rsquo;t expect more though, but what was surprising was that it actually felt like I had to do less work at this time.</p> <p>The assistant also helped adding a <code>--no-parallel</code> flag to the management command. The output was even more broken than the output of the change mentioned above, but again, I could easily fix it to achieve what I wanted.</p> <p>The fact that I know the code and <a href="https://git-scm.com/">git</a> well certainly helped, the assistant would really have helped without that knowledge.</p> <p>In the end, switching to <code>multiprocessing</code> didn&rsquo;t help, but adding the <code>--no-parallel</code> flag allowed them to run the processing themselves by not spawning any additional threads or processes.</p> <p>The energy use and the stealing of copyrighted material done by the AI companies is still really bad. It does feel somewhat OK to use an AI assistant in an area where I&rsquo;m proficient as well and where I probably also supplied training material (without being asked if I wanted this) though. It&rsquo;s making me slightly faster, and doesn&rsquo;t allow me to do things I really couldn&rsquo;t otherwise.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/FeinCMS/">FeinCMS 24.12.3</a>: I have added a TinyMCE 7 integration to FeinCMS.</li> <li><a href="https://pypi.org/project/django-imagefield/">django-imagefield 0.21.1</a>: See above.</li> </ul>Weeknotes (2024 week 51)https://406.ch/writing/weeknotes-2024-week-51/2024-12-20T12:00:00Z2024-12-20T12:00:00Z<h1 id="weeknotes-2024-week-51"><a class="toclink" href="#weeknotes-2024-week-51">Weeknotes (2024 week 51)</a></h1> <h2 id="building-forms-using-django"><a class="toclink" href="#building-forms-using-django">Building forms using Django</a></h2> <p>I last wrote about this topic <a href="https://406.ch/writing/building-forms-with-the-django-admin/">in April</a>. It has <a href="https://mastodon.social/@webology/113669270531953652">resurfaced on Mastodon this week</a>. I&rsquo;m thinking about writing a <a href="https://github.com/feincms/feincms3-forms">feincms3-forms</a> demo app, but I already have too much on my plate. I think composing a forms builder on top of <a href="https://django-content-editor.readthedocs.io/">django-content-editor</a> is the way to go, instead of replacing the admin interface altogether &ndash; sure, you can always do that, but it&rsquo;s so much less composable&hellip;</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/blacknoise/">blacknoise 1.2</a>: No real changes, added support for Python 3.13 basically without changing anything. It&rsquo;s always nice when this happens.</li> <li><a href="https://pypi.org/project/django-imagefield/">django-imagefield 0.21</a></li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.10</a>: I rebuilt django-prose-editor from the ground up <a href="https://406.ch/writing/rebuilding-django-prose-editor-from-the-ground-up/">and wrote about that two weeks ago</a>. The 0.10 release marks the final point of this particular rewrite.</li> <li><a href="https://pypi.org/project/django-js-asset/">django-js-asset 3.0</a>: See the blog post from <a href="https://406.ch/writing/object-based-assets-for-django-s-forms-media/">this week</a></li> </ul>Weeknotes (2024 week 49)https://406.ch/writing/weeknotes-2024-week-49/2024-12-06T12:00:00Z2024-12-06T12:00:00Z<h1 id="weeknotes-2024-week-49"><a class="toclink" href="#weeknotes-2024-week-49">Weeknotes (2024 week 49)</a></h1> <h2 id="django-steering-council-elections"><a class="toclink" href="#django-steering-council-elections">Django Steering Council elections</a></h2> <p>I have been thinking long and hard about running for the Django Steering Council. I think there are a few things I could contribute since I&rsquo;ve been using Django for 16 or more years, and have been working on, maintaining and publishing third-party apps almost all this time. I have also contributed a few small features to Django core itself, and contributed my fair share of tests and bugfixes. The reason why I haven&rsquo;t been more involved was always that I feared the review process with what I perceive to be a too unrestrained perfectionism. Teaching people is good, but I fear that those who teach are self-selected survivors of the process, which come to appreciate the perfectionism a bit too much. It&rsquo;s somewhat the same as with the Swiss naturalization process &ndash; the hurdles are very high, and some of those who weather the process maybe are or grow to be too fond of it.</p> <p>An important point is that this has nothing to do with being nice (or not). Everybody has always been great, maybe with the exception of myself back when I didn&rsquo;t understand that the problem wasn&rsquo;t the individuals but the way everyone has agreed things should be done.</p> <p>I&rsquo;m not the only one who thinks that we <a href="https://knowyourmeme.com/memes/we-should-improve-society-somewhat">should improve the process somewhat</a>. So, I&rsquo;m definitely going to look out for candidates who think this is important.</p> <p>There are a few reasons why I&rsquo;m not running myself at this time. A somewhat important reason is that my candidacy wouldn&rsquo;t help diversity at all. This shouldn&rsquo;t discourage anyone else with the same background from running &ndash; we cannot change the world all at once. More importantly, I have more personal reasons for being hesitant to accept new commitments. That being said, I&rsquo;m looking forward to be more involved in the community in other ways. And also, it&rsquo;s not now or never.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/feincms3-cookiecontrol/">feincms3-cookiecontrol 1.5.4</a>: No functional changes, only code golfing. It&rsquo;s nice to have a working cookie banner with a solution for embedding third party content only when people consent in less than 4KiB of minified (not compressed!) JavaScript.</li> <li><a href="https://pypi.org/project/django-admin-ordering/">django-admin-ordering 0.20</a>: Objects can now be reordered using arrow buttons instead of drag drop or manually changing the ordering field&rsquo;s value. This should make the package more accessible. It&rsquo;s always a joy when people contribute such useful improvements.</li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.10a?</a>: See <a href="https://406.ch/writing/rebuilding-django-prose-editor-from-the-ground-up/">the recent blog post</a>.</li> </ul>Weeknotes (2024 week 47)https://406.ch/writing/weeknotes-2024-week-47/2024-11-20T12:00:00Z2024-11-20T12:00:00Z<h1 id="weeknotes-2024-week-47"><a class="toclink" href="#weeknotes-2024-week-47">Weeknotes (2024 week 47)</a></h1> <p>I missed a single co-writing session and of course that lead to four weeks of no posts at all to the blog. Oh well.</p> <h2 id="debugging"><a class="toclink" href="#debugging">Debugging</a></h2> <p>I want to share a few debugging stories from the last weeks.</p> <h3 id="pillow-11-and-djangos-get_image_dimensions"><a class="toclink" href="#pillow-11-and-djangos-get_image_dimensions">Pillow 11 and Django&rsquo;s <code>get_image_dimensions</code></a></h3> <p>The goal of <a href="https://github.com/matthiask/django-imagefield">django-imagefield</a> was to deeply verify that Django and Pillow are able to work with uploaded files; some files can be loaded, their dimensions can be inspected, but problems happen later when Pillow actually tries resizing or filtering files. Because of this django-imagefield does more work when images are added to the system instead of working around it later. (Django doesn&rsquo;t do this on purpose because doing all this work up-front could be considered a DoS factor.)</p> <p>In the last weeks I suddenly got recurring errors from saved files again, something which shouldn&rsquo;t happen, but obviously did.</p> <p>Django wants to read image dimensions when accessing or saving image files (by the way, always use <code>height_field</code> and <code>width_field</code>, otherwise Django will open and inspect image files even when you&rsquo;re only loading Django models from the database&hellip;!) and it uses a smart and wonderful<sup id="fnref:fn1"><a class="footnote-ref" href="#fn:fn1">1</a></sup> hack to do this: It reads a few hundred bytes from the image file, instructs Pillow to inspect the file and if an exception happens it reads more bytes and tries again. This process relies on the exact type of exceptions raised internally though, and the release of Pillow 11 changed the types&hellip; for some file types only. Fun times.</p> <p>The issue had already been reported as <a href="https://code.djangoproject.com/ticket/33240">#33240</a> and is now tracked as <a href="https://github.com/python-pillow/Pillow/issues/8530">#8530</a> on the Pillow issue tracker. Let&rsquo;s see what happens. For now, django-imagefield declares itself to be incompatible with Pillow 11.0.0 so that this error cannot happen.</p> <h3 id="rspack-and-lightningcss-shuffled-css-properties"><a class="toclink" href="#rspack-and-lightningcss-shuffled-css-properties">rspack and lightningcss shuffled CSS properties</a></h3> <p><a href="https://rspack.dev/">rspack</a> 1.0 started reordering CSS properties which of course lead to CSS properties overriding each other in the incorrect order. That was a fun one to debug. I tracked the issue down to the switch from the swc CSS minimizer to <a href="https://github.com/parcel-bundler/lightningcss">lightningcss</a> and submitted a reproduction to the <a href="https://github.com/parcel-bundler/lightningcss/issues/805#issuecomment-2358219597">issue tracker</a>. My rust knowledge wasn&rsquo;t up to the task of attempting to submit a fix myself. Luckily, it has been fixed in the meantime.</p> <h3 id="rspack-problems"><a class="toclink" href="#rspack-problems">rspack problems</a></h3> <p>I have another problem with rspack where I haven&rsquo;t yet tracked down the issue. rspack produces a broken bundle starting with <a href="https://github.com/web-infra-dev/rspack/releases/tag/v1.0.0-beta.2">1.0.0-beta.2</a> when compiling a particular project of mine. I have the suspicion that I have misconfigured some stuff related to import paths and yarn workspaces. I have no idea how anyone could have a complete understanding of these things&hellip;</p> <p><strong>Update:</strong> The problem was <a href="https://github.com/web-infra-dev/rspack/issues/8027">#8027</a>, <code>experiments.css</code> is quite broken for now.</p> <p>Bundlers are complex beasts, and I&rsquo;m happy that I mostly can just use them.</p> <h3 id="closing-thoughts"><a class="toclink" href="#closing-thoughts">Closing thoughts</a></h3> <p>Debugging is definitely a rewarding activity for me. I like tracking stuff down like this. Unfortunately, problems always tend to crop up when time is scarce already, but what can you do.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <p>Quite a few releases, many of them verifying Python 3.13 and Django 5.1 support (if it hasn&rsquo;t been added already in previous releases). The nicest part: If I remember correctly I didn&rsquo;t have to change anything anywhere, everything just continues to work.</p> <ul> <li><a href="https://pypi.org/project/django-admin-ordering/">django-admin-ordering 0.19</a>: I added support for automatically renumbering objects on page load. This is mostly useful if you already have existing data which isn&rsquo;t ordered yet.</li> <li><a href="https://pypi.org/project/feincms3-data/">feincms3-data 0.7</a>: Made sure that objects are dumped in a deterministic order when dumping. I wanted to compare JSON dumps by hand before and after a big data migration in a customer project and differently ordered dumps made the comparison impossible.</li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.9</a>: I updated the ProseMirror packages, and put the editor into read-only mode for <code>&lt;textarea disabled&gt;</code> elements.</li> <li><a href="https://pypi.org/project/feincms3-language-sites/">feincms3-language-sites 0.4</a>: Finally released the update containing the necessary hook to validate page trees and their unique paths before moving produces integrity errors. Error messages are nicer than internal server errors.</li> <li><a href="https://pypi.org/project/django-authlib/">django-authlib 0.17.2</a>: The value of the cookie which is used to save the URL where users should be redirected to after authentication wasn&rsquo;t checked for validity when setting it, only when reading it. This meant that attackers could produce invalid header errors in application servers. No real security problem here when using authlib&rsquo;s code.</li> <li><a href="https://pypi.org/project/feincms3/">feincms3 5.3</a>: Minor update which mostly removes support for outdated Python and Django versions.</li> <li><a href="https://pypi.org/project/django-imagefield/">django-imagefield 0.20</a>: See above.</li> </ul> <div class="footnote"> <hr /> <ol> <li id="fn:fn1"> <p>wonderfully ugly&#160;<a class="footnote-backref" href="#fnref:fn1" title="Jump back to footnote 1 in the text">&#8617;</a></p> </li> </ol> </div>Weeknotes (2024 week 43)https://406.ch/writing/weeknotes-2024-week-43/2024-10-23T12:00:00Z2024-10-23T12:00:00Z<h1 id="weeknotes-2024-week-43"><a class="toclink" href="#weeknotes-2024-week-43">Weeknotes (2024 week 43)</a></h1> <p>I had some much needed time off, so this post isn&rsquo;t that long even though <a href="https://406.ch/writing/weeknotes-2024-week-39/">four weeks have passed since the last entry</a>.</p> <h2 id="from-webpack-to-rspack"><a class="toclink" href="#from-webpack-to-rspack">From webpack to rspack</a></h2> <p>I&rsquo;ve been really happy with <a href="https://rspack.dev/">rspack</a> lately. Converting webpack projects to rspack is straightforward since it mostly supports the same configuration, but it&rsquo;s much much faster since it&rsquo;s written in Rust. Rewriting things in Rust is a recurring theme, but in this case it really helps a lot. Building the frontend of a larger project of ours consisting of several admin tools and complete frontend implementations for different teaching materials only takes 10 seconds now instead of several minutes. That&rsquo;s a big and relevant difference.</p> <p>Newcomers should probably still either use <a href="https://rsbuild.dev/">rsbuild</a>, <a href="https://vite.dev/">Vite</a> or maybe no bundler at all. Vanilla JS and browser support for ES modules is great. That being said, I like cache busting, optimized bundling and far-future expiry headers in production and hot module reloading in development a lot, so learning to work with a frontend bundler is definitely still worth it.</p> <h2 id="dark-mode-and-light-mode"><a class="toclink" href="#dark-mode-and-light-mode">Dark mode and light mode</a></h2> <p>I have been switching themes in my preferred a few times per year in the past. The following ugly bit of vimscript helps switch me the theme each time the sun comes out when working outside:</p> <div class="chl"><pre><span></span><code><span class="nv">let</span><span class="w"> </span><span class="nv">t</span>:<span class="nv">light</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span> <span class="nv">function</span><span class="o">!</span><span class="w"> </span><span class="nv">FiatLux</span><span class="ss">()</span> <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nv">t</span>:<span class="nv">light</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span> <span class="w"> </span>:<span class="nv">set</span><span class="w"> </span><span class="nv">background</span><span class="o">=</span><span class="nv">light</span> <span class="w"> </span><span class="nv">let</span><span class="w"> </span><span class="nv">t</span>:<span class="nv">light</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span> <span class="w"> </span><span class="k">else</span> <span class="w"> </span>:<span class="nv">set</span><span class="w"> </span><span class="nv">background</span><span class="o">=</span><span class="nv">dark</span> <span class="w"> </span><span class="nv">let</span><span class="w"> </span><span class="nv">t</span>:<span class="nv">light</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span> <span class="w"> </span><span class="k">endif</span> <span class="nv">endfunction</span> <span class="nv">nnoremap</span><span class="w"> </span><span class="o">&lt;</span><span class="nv">F12</span><span class="o">&gt;</span><span class="w"> </span>:<span class="k">call</span><span class="w"> </span><span class="nl">FiatLux</span><span class="ss">()</span><span class="o">&lt;</span><span class="nv">CR</span><span class="o">&gt;</span> </code></pre></div> <p>I&rsquo;m using the <a href="https://devsuite.app/ptyxis/">Ptyxis</a> terminal emulator currently, I haven&rsquo;t investigated yet if there&rsquo;s a shortcut to toggle dark and light mode for it as well. Using F10 to open the main menu works fine though, and using the mouse wouldn&rsquo;t be painful either.</p> <h2 id="helping-out-in-the-django-forum-and-the-discord"><a class="toclink" href="#helping-out-in-the-django-forum-and-the-discord">Helping out in the Django forum and the Discord</a></h2> <p>I have found some pleasure in helping out in the <a href="https://forum.djangoproject.com/">Django Forum</a> and in the official <a href="https://discord.gg/xcRH6mN4fa">Django Discord</a>. I sometimes wonder why more people aren&rsquo;t reading the Django source code when they hit something which looks like a bug or something which they do not understand. I find Django&rsquo;s source code very readable and I have found many nuggets within it. I&rsquo;d always recommend checking the documentation or maybe official help channels first, but the code is also out there and that fact should be taken advantage of.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/django-content-editor/">django-content-editor 7.1</a>: Fixed a bug where the ordering and region fields were handled incorrectly when they appear on one line in the fieldset. Also improved the presentation of inlines in unknown regions and clarified the meaning of the move to region dropdown. Also, released the improvements from previous patch releases as a new minor release because that&rsquo;s what I should have been doing all along.</li> <li><a href="https://pypi.org/project/form-designer/">form-designer 0.27</a>: A user has been bitten by <code>slugify</code> removing cyrillic characters because it only keeps ASCII characters around. Here&rsquo;s the wontfixed bug in the Django issue tracker: <a href="https://code.djangoproject.com/ticket/8391">#8391</a>. I fixed the issue by removing the slugification (is that even a word?) when generating choices.</li> </ul>Weeknotes (2024 week 39)https://406.ch/writing/weeknotes-2024-week-39/2024-09-25T12:00:00Z2024-09-25T12:00:00Z<h1 id="weeknotes-2024-week-39"><a class="toclink" href="#weeknotes-2024-week-39">Weeknotes (2024 week 39)</a></h1> <h2 id="css-for-django-forms"><a class="toclink" href="#css-for-django-forms">CSS for Django forms</a></h2> <p>Not much going on in OSS land. I have been somewhat active in the official Django forum, discussing ways to add Python-level hooks to allow adding CSS classes around form fields and their labels. The discussion on the <a href="https://forum.djangoproject.com/t/proposal-make-it-easy-to-add-css-classes-to-a-boundfield/32022">forum</a> and on the <a href="https://github.com/django/django/pull/18266">pull request</a> goes in the direction of allowing using custom <code>BoundField</code> classes per form or even per project (instead of only per field as is already possible today). This would allow overriding <code>css_classes</code>, e.g. to add a simple <code>class="field"</code>. Together with <code>:has()</code> this would probably allow me to skip using custom HTML templates in 99% of all cases.</p> <p>I have also been lurking in the Discord, but more to help and less to promote my packages and ideas :-)</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/django_user_messages/">django-user-messages 1.1</a>: Added Django 5.0, 5.1 to the CI, and fixed the migrations to no longer mention <code>index_together</code> at all. It seems that squashing the migrations wasn&rsquo;t sufficient, I also had to actually delete the old migrations.</li> <li><a href="https://pypi.org/project/blacknoise/">blacknoise 1.1</a>: <a href="https://www.starlette.io/">Starlette</a>&rsquo;s <code>FileResponse</code> has gained support for the HTTP Range header, allowing me to remove my homegrown implementation from the package. The blacknoise implementation is now half as long as it was in 1.0.</li> <li><a href="https://pypi.org/project/django_fhadmin/">django-fhadmin 2.3</a>: No new features, only tweaks to the styling and behavior prompted by updates to Django&rsquo;s admin interface.</li> <li><a href="https://pypi.org/project/django-cabinet/">django-cabinet 0.17</a>: I have pruned the CI matrix and accepted a pull request adding a ru translation. I feel conflicted about that since I strongly believe that everything is political, but I don&rsquo;t know if rejecting translations helps anyone.</li> </ul>Weeknotes (2024 week 37)https://406.ch/writing/weeknotes-2024-week-37/2024-09-11T12:00:00Z2024-09-11T12:00:00Z<h1 id="weeknotes-2024-week-37"><a class="toclink" href="#weeknotes-2024-week-37">Weeknotes (2024 week 37)</a></h1> <h2 id="django-debug-toolbar-alpha-with-async-support"><a class="toclink" href="#django-debug-toolbar-alpha-with-async-support">django-debug-toolbar alpha with async support!</a></h2> <p>I have helped mentoring Aman Pandey who has worked all summer to add async support to <a href="https://github.com/jazzband/django-debug-toolbar/">django-debug-toolbar</a>. <a href="https://github.com/tim-schilling">Tim</a> has released an alpha which contains all of the work up to a few days ago. Test it! Let&rsquo;s find the breakages before the final release.</p> <h2 id="dropping-python-39-from-my-projects"><a class="toclink" href="#dropping-python-39-from-my-projects">Dropping Python 3.9 from my projects</a></h2> <p>I have read <a href="https://noumenal.es/posts/the-only-green-python/yLw/">Carlton&rsquo;s post about the only green Python release</a> and have started dropping Python 3.9 support from many of the packages I maintain. This is such a good point:</p> <blockquote> <p>[&hellip;] I’m also thinking about it in terms of reducing the number of Python versions we support in CI. It feels like a lot of trees to support 5 full versions of Python for their entire life. 🌳</p> </blockquote> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/django-debug-toolbar/5.0.0a0/">django-debug-toolbar 5.0.0a0</a>: See above.</li> <li><a href="https://pypi.org/project/form-designer/">form-designer 0.26.2</a>: The values of choice fields are now returned as-is when sending mails or exporting form submissions instead of only returning the slugified version.</li> <li><a href="https://pypi.org/project/django-authlib/">django-authlib 0.17.1</a>: The <a href="https://406.ch/writing/keep-content-managers-admin-access-up-to-date-with-role-based-permissions/">role-based permissions backend</a> had a bug where it wouldn&rsquo;t return all available permissions in all circumstances, leading to empty navigation sidebars in the Django administration. This has been fixed.</li> <li><a href="https://pypi.org/project/feincms3/">feincms3 5.2.3</a>: Bugfix release, the page moving interface is no longer hidden by an expanded navigation sidebar. I almost always turn off the sidebar in my projects so I haven&rsquo;t noticed this.</li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.8.1</a>: Contains the most recent ProseMirror updates and bugfixes.</li> <li><a href="https://pypi.org/project/django-content-editor/">django-content-editor 7.0.10</a>: Now supports sections. Separate blog post coming up!</li> </ul>Weeknotes (2024 week 35)https://406.ch/writing/weeknotes-2024-week-35/2024-08-28T12:00:00Z2024-08-28T12:00:00Z<h1 id="weeknotes-2024-week-35"><a class="toclink" href="#weeknotes-2024-week-35">Weeknotes (2024 week 35)</a></h1> <h2 id="getting-deep-into-htmx-and-django-template-partials"><a class="toclink" href="#getting-deep-into-htmx-and-django-template-partials">Getting deep into htmx and django-template-partials</a></h2> <p>I have been skeptical about <a href="https://htmx.org/">htmx</a> for some time because basically everything the library does is straightforward to do myself with a few lines of JavaScript. I am a convert now because, really, adding a few HTML attributes is nicer than copy pasting a few lines of JavaScript. Feels good.</p> <p>The combination of htmx with <a href="https://github.com/carltongibson/django-template-partials/">django-template-partials</a> is great as well. I didn&rsquo;t know I had been missing template partials until I started using them. Includes are still useful, but replacing some of them with partials makes working on the project much more enjoyable.</p> <p>I haven&rsquo;t yet had a use for <a href="https://django-htmx.readthedocs.io/">django-htmx</a> but I may yet surprise myself.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/django-authlib/">django-authlib 0.17</a>: django-authlib bundles <code>authlib.little_auth</code> which offers an user model which uses the email address as the username. I have also introduced the concept of <a href="https://406.ch/writing/keep-content-managers-admin-access-up-to-date-with-role-based-permissions/">roles instead of permissions</a>; now I have reorganized the user admin fieldset to hide user permissions altogether. Group permissions are still available as are roles. I&rsquo;m personally convinced that user permissions were a mistake.</li> <li><a href="https://pypi.org/project/feincms3-forms/">feincms3-forms 0.5</a>: Allowed setting a maximum length for the bundled URL and email fields through the Django administration interface.</li> <li><a href="https://pypi.org/project/django-content-editor/">django-content-editor 7.0.7</a>: Fixed a bug where plugins with several fieldsets weren&rsquo;t collapsed completely.</li> <li><a href="https://pypi.org/project/django-imagefield/">django-imagefield 0.19.1</a>: Allowed deactivating autogeneration of thumbnails completely through the <code>IMAGEFIELD_AUTOGENERATE</code> setting. This is very useful for batch processing. Also, documented all available settings.</li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.8</a>: Added support for translating the interface elements and for restricting the available heading levels in the UI.</li> <li><a href="https://pypi.org/project/form-designer/">form-designer 0.25</a>: Fixed the type of the author field for the send-to-author processing action.</li> <li><a href="https://pypi.org/project/feincms3/">feincms3 5.2.2</a>: Added support for embedding <a href="https://www.srf.ch/play/tv">SRF play</a> external content. They do not support oEmbed unfortunately.</li> <li><a href="https://pypi.org/project/feincms3-cookiecontrol/">feincms3-cookiecontrol 1.5.3</a>: Added support for SRF play embeddings as well. The difference is that the feincms3-cookiecontrol embedding requires consent before embedding external content.</li> </ul>Weeknotes (2024 week 33)https://406.ch/writing/weeknotes-2024-week-33/2024-08-14T12:00:00Z2024-08-14T12:00:00Z<h1 id="weeknotes-2024-week-33"><a class="toclink" href="#weeknotes-2024-week-33">Weeknotes (2024 week 33)</a></h1> <h2 id="partying"><a class="toclink" href="#partying">Partying</a></h2> <p>It&rsquo;s summer, it&rsquo;s hot, and it&rsquo;s dance week. <a href="https://lethargy.ch/">Lethargy</a> is over, <a href="https://www.junglestreetgroove.ch/">Jungle Street Groove</a> is coming up. Good times.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/django-json-schema-editor/">django-json-schema-editor 0.1</a>: I have finally left the alpha versioning. I&rsquo;m still not committing to backwards compatibility, but I have started writing a CHANGELOG.</li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.7.1</a>: Thanks to Carlton&rsquo;s pull request I have finally cleaned up the CSS somewhat and made overriding the styles more agreeable when using the editor outside the Django administration. The confusing active state of menubar buttons has also been rectified. <a href="https://django-prose-editor.readthedocs.io/">Docs are now available on Read the Docs.</a></li> <li><a href="https://pypi.org/project/django-imagefield/">django-imagefield 0.19</a>: django-imagefield can now be used with proxy models. Previously, thumbnails weren&rsquo;t generated or deleted when saving proxy models because the signal handlers would only be called if the <code>sender</code> matches exactly. I have already debugged this before, but have forgotten about it again. The ticket is really old for this, and fixing it isn&rsquo;t easy since it&rsquo;s unclear what should happen (<a href="https://code.djangoproject.com/ticket/9318">#9318</a>).</li> <li><a href="https://pypi.org/project/django_canonical_domain/">django-canonical-domain 0.11</a>: django-canonical-domain has gained support for excluding additional domains from the canonical domain redirect. django-canonical-domain is used to redirect users to HTTPS (optionally) and to a particular canonical domain (as the name says). But sometimes you have auxiliary domains, e.g. for an API service, which shouldn&rsquo;t be redirected. The package can now be used in these scenarios as well.</li> <li><a href="https://pypi.org/project/FeinCMS/">FeinCMS 24.8.2</a>: The venerable FeinCMS, now more than 15 years old. The thumbnailing support had a bug where it tried saving JPEGs using RGBA (which obviously doesn&rsquo;t work). This has been fixed.</li> </ul>Weeknotes (2024 week 31)https://406.ch/writing/weeknotes-2024-week-31/2024-07-31T12:00:00Z2024-07-31T12:00:00Z<h1 id="weeknotes-2024-week-31"><a class="toclink" href="#weeknotes-2024-week-31">Weeknotes (2024 week 31)</a></h1> <p>I have missed almost two months of weeknotes. I&rsquo;ve got some catching up to do. I have tried writing a larger piece on my thoughts about CMS, but with everything going on in my personal and work life I haven&rsquo;t made much progress.</p> <p>This weeknotes entry is me trying to get back into the groove of writing (and publishing!) regularly.</p> <h2 id="django-prose-editor"><a class="toclink" href="#django-prose-editor"><a href="https://github.com/matthiask/django-prose-editor/">django-prose-editor</a></a></h2> <p>I have previously written about the <a href="https://prosemirror.net/">ProseMirror</a>-based editor for Django websites <a href="https://406.ch/writing/django-prose-editor-prose-editing-component-for-the-django-admin/">here</a>. I have continued working on the project in the meantime. Apart from bugfixes the big new feature is the support for showing typographic characters. For now the editor supports showing non-breaking spaces and soft hyphens. The project seems to get a little more interest after the deprecation of django-ckeditor has become more well known and the project has even received a contribution by someone else. It&rsquo;s always a lovely moment when this happens.</p> <h2 id="django-json-schema-editor"><a class="toclink" href="#django-json-schema-editor"><a href="https://github.com/matthiask/django-json-schema-editor">django-json-schema-editor</a></a></h2> <p>Still alpha. Updated the vendorized JSON editor and fixed the integration into Django to not throw errors with the newer version.</p> <p>Foreign key fields now support describing the referenced value similar to the raw ID fields functionality. Added optional support for using <code>"format": "prose"</code> to use the django-prose-editor to edit individual fields. JSON plugins for the content editor are now downcasted into their proxy models automatically. This is especially useful with the feincms3 changes mentioned below. (You do not have to use either django-content-editor or feincms3 to use this package!)</p> <p>The following screenshot shows the prose editor integration; the mentioned foreign key field description isn&rsquo;t visible yet here.</p> <p><img alt="A screenshot of the JSON editor including the prose editor" src="https://406.ch/assets/20240731-json-editor.png" /></p> <h2 id="traduire"><a class="toclink" href="#traduire"><a href="https://github.com/matthiask/traduire">Traduire</a></a></h2> <p>Traduire (french for «translate») is a web-based platform for editing gettext translations.</p> <p>It is intended as a replacement for Transifex, Weblate and comparable products. It is geared towards small teams or agencies which want to allow their customers and their less technical team members to update translations.</p> <p>Traduire profits from the great work done on <a href="https://github.com/mbi/django-rosetta/">django-rosetta</a>. I would still be using Rosetta if it would work when used with a container orchestator such as Kubernetes. Since all application storage is ephemeral that doesn&rsquo;t work, translation editing and deployment have to be separated.</p> <p><img alt="A screenshot of the Traduire interface" src="https://406.ch/assets/20240731-traduire.png" /></p> <p>It is built using Django and relies on <a href="https://pypi.org/project/polib/">polib</a> to do the heavy lifting.</p> <p>This is a project which might also be interesting for others. I would especially appreciate it if someone could contribute an easier way to get it up and running, e.g. using a Docker Compose configuration or something. I am using Kubernetes and GitOps to host it, but that&rsquo;s not straightforward at all. Really, all that&rsquo;s needed to run it is a Django host with any database which is supported by Django. I prefer using PostgreSQL because I have it, but sqlite etc. work just as well.</p> <h2 id="releases-since-the-second-week-of-june"><a class="toclink" href="#releases-since-the-second-week-of-june">Releases since the second week of June</a></h2> <ul> <li><a href="https://pypi.org/project/django-translated-fields/">django-translated-fields 0.13</a>: Nothing much except for CI and pre-commit updates. The implementation continues to be rock-solid and basically unchanged.</li> <li><a href="https://pypi.org/project/django-content-editor/">django-content-editor 7.0.6</a>: Tweaks and fixes to the new interface. Added better scrolling behavior when dragging content around. The editor now also supports colorized icons which helps quickly understanding the structure of some content when there are many plugins.</li> <li><a href="https://pypi.org/project/blacknoise/">blacknoise 1.0.2</a>: Fixed a few bugs in the <code>blacknoise.compress</code> utility and started running the testsuite on GitHub actions. <a href="https://github.com/evansd/whitenoise/">whitenoise</a> has been friendly-forked as <a href="https://github.com/Archmonger/ServeStatic">ServeStatic</a> and I&rsquo;m definitely having a close look at this project as well, but blacknoise is simple and works well, so I&rsquo;m not convinced that switching back to the much larger project (in terms of amounts of code) is an improvement now.</li> <li><a href="https://pypi.org/project/django-authlib/">django-authlib</a>: Minor bugfixes.</li> <li><a href="https://pypi.org/project/feincms3/">feincms3</a>: Allowed registering plugin models with the renderer which aren&rsquo;t supposed to be fetched from the database. This is especially useful when used together with JSON plugins, where the individual JSON plugins are created as proxies for the underlying Django model and fetched all at once. Disabled the version check on our CKEditor plugin. Still, really stop using CKEditor 4 if you want to use maintained software.</li> <li><a href="https://pypi.org/project/FeinCMS/">FeinCMS 24.7.1</a>: Small bugfixes, and made the Read the Docs build work correctly.</li> <li><a href="https://pypi.org/project/django-debug-toolbar/">django-debug-toolbar 4.4.x</a>: The toolbar continues to be a nice project to work on. Fixed a few edge cases in the new alerts panel.</li> <li><a href="https://pypi.org/project/django-admin-ordering/">django-admin-ordering 0.18.2</a>: The value of <code>ordering_field</code> now has additional sanity checks.</li> <li><a href="https://pypi.org/project/django-cabinet/">django-cabinet 0.16</a>: cabinet now supports exporting a folder as a ZIP file while preserving the structure you see in the CMS instead of the structure on the file system. The inline upload form has been dropped from the <code>CabinetForeignKey</code> widget because the folder dropdown slowed down the page a lot when used on a site with many folders. Using the raw ID fields popup isn&rsquo;t that bad.</li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.6.2</a>: See above.</li> <li><a href="https://pypi.org/project/django-json-schema-editor/">django-json-schema-editor 0.0.28</a>: See above.</li> </ul>Weeknotes (2024 week 23)https://406.ch/writing/weeknotes-2024-week-23/2024-06-07T12:00:00Z2024-06-07T12:00:00Z<h1 id="weeknotes-2024-week-23"><a class="toclink" href="#weeknotes-2024-week-23">Weeknotes (2024 week 23)</a></h1> <h2 id="switching-everything-from-pip-to-uv"><a class="toclink" href="#switching-everything-from-pip-to-uv">Switching everything from pip to uv</a></h2> <p>Enough said. I&rsquo;m always astonished how fast computers can be.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/django-admin-ordering/">django-admin-ordering 0.18</a>: Added a database index to the ordering field since we&rsquo;re always sorting by it.</li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.4</a>: Dropped the jQuery dependency making it possible to use the editor outside the Django administration interface without annoying JavaScript errors. Allowed additional heading levels and moved the block type buttons into a popover.</li> <li><a href="https://pypi.org/project/django-debug-toolbar/">django-debug-toolbar 4.4.2</a>: I enjoy working on this important piece of software very much.</li> <li><a href="https://pypi.org/project/django-email-hosts/">django-email-hosts 0.2.1</a>: Added a command analogous to <code>./manage.py sendtestemail</code> so that it&rsquo;s possible to easily test the different configured email backends.</li> <li><a href="https://pypi.org/project/feincms3/">feincms3 5.0</a>: I completely reworked the move node action; previously it opened a new page where you could see all possible targets; now you can cut a page and paste it somewhere else. The advantages of the new interface is that you don&rsquo;t leave the changelist and can still profit from all its features while moving pages around.</li> <li><a href="https://pypi.org/project/feincms3-sites/">feincms3-sites 0.21</a>: A new release taking advantage of a new hook in feincms3 7.0 so that the new moving interface works.</li> <li><a href="https://pypi.org/project/django-authlib/">django-authlib 0.16.5</a>: authlib now shows a welcome message when authenticating using admin OAuth2. It&rsquo;s nice and helps with debugging strange authentication failures.</li> <li><a href="https://pypi.org/project/django-content-editor/">django-content-editor 7.0</a>: I reworked the UI. The sidebar is gone, instead there are nice buttons in the place where you can add new plugins; the plugins appear in a nice grid instead of a list, which looks much better once you have more than just a few plugin types available. Also, plugin type icons are now shown in the plugin forms. I think it looks much better than before.</li> <li><a href="https://pypi.org/project/feincms3-cookiecontrol/">feincms3-cookiecontrol 1.5.2</a>: I didn&rsquo;t contribute anything to this release which is also a nice experience for a change. The Google consent mode integration has been improved and simplified.</li> <li><a href="https://pypi.org/project/django-json-schema-editor/">django-json-schema-editor 0.0.22</a>: Various small-ish improvements. I should really start using higher version numbers, but not having to commit to anything also feels great. That being said, the editor is in active use in several projects, so maybe I&rsquo;m deceiving myself.</li> </ul>Weeknotes (2024 week 21)https://406.ch/writing/weeknotes-2024-week-21/2024-05-22T12:00:00Z2024-05-22T12:00:00Z<h1 id="weeknotes-2024-week-21"><a class="toclink" href="#weeknotes-2024-week-21">Weeknotes (2024 week 21)</a></h1> <p>There have been times when work has been more enjoyable than in the last few weeks. It feels more stressful than at other times, and this mostly has to do with particular projects. I hope I&rsquo;ll be able to move on soon.</p> <h2 id="blacknoise"><a class="toclink" href="#blacknoise">blacknoise</a></h2> <p>I have released <a href="https://pypi.org/project/blacknoise/">blacknoise 1.0</a>. It&rsquo;s an ASGI app for static file serving inspired by <a href="https://github.com/evansd/whitenoise/">whitenoise</a>.</p> <p>The 1.0 version number is only a big step in versioning terms, not much has happened with the code. It&rsquo;s a tiny little well working piece of software which has been running in production for some time without any hickups. The biggest recent change is that I have parallelized the gzip and brotli compression step; this makes building images using whitenoise more painful because there the wait is really really long sometimes. <a href="https://github.com/evansd/whitenoise/pull/484">A pull request fixing this exists</a>, but it hasn&rsquo;t moved forwards in months.</p> <p>I have written a longer post about it earlier this year <a href="https://406.ch/writing/blacknoise-asgi-app-for-static-file-serving/">here</a>.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/feincms3-cookiecontrol/">feincms3-cookiecontrol 1.5</a>: Code golfing. Added backwards compatibility with old Django versions so that I can use it for old projects. Also includes optional support for Google consent management.</li> <li><a href="https://pypi.org/project/django_fast_export/">django-fast-export 0.1.1</a>: This is basically a repackaging of the streaming CSV view from Django&rsquo;s documentation as a reusable class. I have switched to using an iterator so that I can export even larger datasets.</li> <li><a href="https://pypi.org/project/django-json-schema-editor/">django-json-schema-editor 0.0.18</a>: Still alpha versioned but used in production in various projects. I should really release an 1.0 version, but there are no integration tests at all. Mainly visual tweaks in this update.</li> <li><a href="https://pypi.org/project/django-content-editor/">django-content-editor 6.5</a>: Better handling of templates and regions when a particular editor instance only shows a subset of configured templates. Disallowed adding plugins when in an unknown region. It&rsquo;s funny how many edge cases exist in software as old as this.</li> <li><a href="https://pypi.org/project/blacknoise/">blacknoise 1.0</a>: See above.</li> <li><a href="https://pypi.org/project/html-sanitizer/">html-sanitizer 2.4.4</a>: Fixed edge cases with whitespace handling when merging elements.</li> <li><a href="https://pypi.org/project/feincms3-data/">feincms3-data 0.6.1</a>: Allowed <code>./manage.py f3loaddata -</code> to load JSON data from stdin.</li> </ul>Weeknotes (2024 week 18)https://406.ch/writing/weeknotes-2024-week-18/2024-05-03T12:00:00Z2024-05-03T12:00:00Z<h1 id="weeknotes-2024-week-18"><a class="toclink" href="#weeknotes-2024-week-18">Weeknotes (2024 week 18)</a></h1> <h2 id="google-summer-of-code-has-begun"><a class="toclink" href="#google-summer-of-code-has-begun">Google Summer of Code has begun</a></h2> <p>We have a student helping out with adding async support to the <a href="https://github.com/jazzband/django-debug-toolbar/">Django Debug Toolbar</a>. It&rsquo;s great that someone can spend some concentrated time to work on this. Tim and others have done all the necessary preparation work, I&rsquo;m only helping from the sidelines so don&rsquo;t thank me.</p> <h2 id="bike-to-work"><a class="toclink" href="#bike-to-work">Bike to Work</a></h2> <p>Two teams from my company are participating in the <a href="https://www.biketowork.ch/">Bike to Work Challenge 2024</a>. It&rsquo;s what I do anyway (if I&rsquo;m not working from home) but maybe it helps build others some motivation to get on the bicycle once more. Public transports in the city where I live are great but I&rsquo;ll always take the bike when I can. I also went on my first mountain bike ride in a few months yesterday, good fun.</p> <h2 id="json-blobs-and-referential-integrity"><a class="toclink" href="#json-blobs-and-referential-integrity">JSON blobs and referential integrity</a></h2> <p>The <a href="https://github.com/matthiask/django-json-schema-editor/">django-json-schema-editor</a> has gained support for referencing Django models. Here&rsquo;s an example schema excerpt:</p> <div class="chl"><pre><span></span><code><span class="p">{</span> <span class="w"> </span><span class="o">...</span> <span class="w"> </span><span class="s">&quot;articles&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="s">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;array&quot;</span><span class="p">,</span> <span class="w"> </span><span class="s">&quot;format&quot;</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;table&quot;</span><span class="p">,</span> <span class="w"> </span><span class="s">&quot;title&quot;</span><span class="p">:</span><span class="w"> </span><span class="nx">_</span><span class="p">(</span><span class="s">&quot;articles&quot;</span><span class="p">),</span> <span class="w"> </span><span class="s">&quot;minItems&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span> <span class="w"> </span><span class="s">&quot;maxItems&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span> <span class="w"> </span><span class="s">&quot;items&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="s">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;string&quot;</span><span class="p">,</span> <span class="w"> </span><span class="s">&quot;title&quot;</span><span class="p">:</span><span class="w"> </span><span class="nx">_</span><span class="p">(</span><span class="s">&quot;article&quot;</span><span class="p">),</span> <span class="w"> </span><span class="s">&quot;format&quot;</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;foreign_key&quot;</span><span class="p">,</span> <span class="w"> </span><span class="s">&quot;options&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="s">&quot;url&quot;</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;/admin/articles/article/?_popup=1&amp;_to_field=id&quot;</span><span class="p">,</span> <span class="w"> </span><span class="p">},</span> <span class="w"> </span><span class="p">},</span> <span class="w"> </span><span class="p">},</span> <span class="w"> </span><span class="o">...</span> <span class="p">}</span> </code></pre></div> <p>The ID field is stringly typed; using an integer directly wouldn&rsquo;t work because the empty string isn&rsquo;t a valid integer.</p> <p>The problem with referencing models in this way is that there&rsquo;s no way to know if the referenced object is still around or not, or even to protect it against deletion. The bundled django-content-editor <code>JSONPlugin</code> now supports automatically generating a <code>ManyToManyField</code> with a <code>through</code> model which protects articles from deletion as long as they are referenced from a <code>JSONPlugin</code> instance. The <code>register_reference</code> line creates the mentioned model with an <code>on_delete=models.PROTECT</code> foreign key to articles and a <code>post_save</code> handler which updates said references.</p> <div class="chl"><pre><span></span><code><span class="kn">from</span><span class="w"> </span><span class="nn">django_json_schema_editor.plugins</span><span class="w"> </span><span class="kn">import</span><span class="w"> </span><span class="n">JSONPluginBase</span><span class="p">,</span><span class="w"> </span><span class="n">register_reference</span> <span class="kn">from</span><span class="w"> </span><span class="nn">articles.models</span><span class="w"> </span><span class="kn">import</span><span class="w"> </span><span class="n">Article</span> <span class="k">class</span><span class="w"> </span><span class="nc">JSONPlugin</span><span class="p">(</span><span class="n">JSONPluginBase</span><span class="p">,</span><span class="w"> </span><span class="o">...</span><span class="p">):</span> <span class="w"> </span><span class="k">pass</span> <span class="n">register_reference</span><span class="p">(</span><span class="n">JSONPlugin</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;articles&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">Article</span><span class="p">)</span> </code></pre></div> <h2 id="releases-since-the-beginning-of-april"><a class="toclink" href="#releases-since-the-beginning-of-april">Releases since the beginning of April</a></h2> <ul> <li><a href="https://pypi.org/project/django-json-schema-editor/">django-json-schema-editor 0.0.14</a>: See above. Also, some styling work and a patch update to the vendorized json-editor.</li> <li><a href="https://pypi.org/project/django-content-editor/">django-content-editor 6.4.6</a>: Many small stylistic fixes. The target indicator when dragging plugins is now also shown when plugins are collapsed. It&rsquo;s now possible to directly drag a plugin to the end, and not just to the second to last position.</li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.3.4</a>: Switched to the nh3 sanitizer because it&rsquo;s faster and because ProseMirror never emits HTML which has to be cleaned up first. Stopped generating menu items for nodes and marks which aren&rsquo;t in the schema. Added the possibility to reduce the functionality per editor instance. Small tweaks and fixes.</li> <li><a href="https://pypi.org/project/django-tree-queries/">django-tree-queries 0.19</a>: Added support for pre-filtering the tree (much more efficient when only querying a part of the tree). Added support for adding additional fields to the CTE so that you can collect values from ancestors for other fields than the default fields too.</li> <li><a href="https://pypi.org/project/FeinCMS/">FeinCMS 24.4.2</a>: Added support for webp images. Fixed a few of the admin list filters to work with Django 5.</li> <li><a href="https://pypi.org/project/django-cabinet/">django-cabinet 0.14.3</a>: Fixed the support for the <code>extra_context</code> argument to our <code>changelist_view</code> implementation.</li> </ul>Weeknotes (2024 week 14)https://406.ch/writing/weeknotes-2024-week-14/2024-04-06T12:00:00Z2024-04-06T12:00:00Z<h1 id="weeknotes-2024-week-14"><a class="toclink" href="#weeknotes-2024-week-14">Weeknotes (2024 week 14)</a></h1> <p>I&rsquo;m having a bit of a slow week with the easter weekend and a wisdom tooth extraction. I&rsquo;m recovering quite quickly it seems and I&rsquo;m glad about it.</p> <p>This weeknotes entry is short and quick. I&rsquo;m trying to get back into the habit of writing them after a mediocre start this year.</p> <h2 id="20th-anniversary-celebration-of-young-greens-switzerland"><a class="toclink" href="#20th-anniversary-celebration-of-young-greens-switzerland">20th Anniversary Celebration of Young Greens Switzerland</a></h2> <p>I have attended the celebration of Young Greens Switzerland. I have been a founding member and have been active for close to ten years. A lot of time has passed since then. It has been great to reminisce about old times with friends and, more importantly, to see how the torch is carried on.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/blacknoise/">blacknoise 0.0.5</a>: blacknoise is an ASGI app for static file serving inspired by <a href="https://whitenoise.readthedocs.io/en/latest/">whitenoise</a>. It only supports a very limited subset of whitenoise&rsquo;s functionality, but it supports async.</li> <li><a href="https://pypi.org/project/html-sanitizer/">html-sanitizer 2.4.1</a>: The lxml library moved the HTML cleaner into its own package, <a href="https://pypi.org/project/lxml-html-clean/">lxml-html-clean</a>; this release adds support for that. I didn&rsquo;t know that the HTML cleaner is viewed as being problematic by the lxml maintainers. I&rsquo;m having another look at <a href="https://github.com/messense/nh3">nh3</a> and will maybe switch html-sanitizer&rsquo;s guts from lxml to nh3 in the future.</li> <li><a href="https://pypi.org/project/django-tree-queries/">django-tree-queries 0.18</a>: django-tree-queries now supports ordering siblings by multiple fields and even allows descending orderings.</li> <li><a href="https://pypi.org/project/django-cabinet/">django-cabinet 0.14.2</a>: This release fixes the CKEditor 4 filebrowser popup when using Django 5 or better.</li> </ul>Weeknotes (2024 week 11)https://406.ch/writing/weeknotes-2024-week-11/2024-03-16T12:00:00Z2024-03-16T12:00:00Z<h1 id="weeknotes-2024-week-11"><a class="toclink" href="#weeknotes-2024-week-11">Weeknotes (2024 week 11)</a></h1> <h2 id="estimates"><a class="toclink" href="#estimates">Estimates</a></h2> <p><a href="https://jacobian.org/2024/mar/11/breaking-down-tasks/">Jacob wrote an excellent post on breaking down tasks</a>. I did like the post a lot. Maybe I&rsquo;ll write a longer reply later, but for now just this. <a href="https://hachyderm.io/@jacob@jacobian.org/112081126379604868">There definitely are good reasons for the pushback against estimation</a>, and it&rsquo;s really not just that some people lack professionalism.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/django-cabinet/">django-cabinet 0.14.1</a>: Mini release containing a Turkish translation. It&rsquo;s always nice if software is used.</li> <li><a href="https://pypi.org/project/feincms3/">feincms3 4.6</a>: Fixed a bug where the move form wouldn&rsquo;t use a potentially overridden <code>ModelAdmin.get_queryset</code> method.</li> <li><a href="https://pypi.org/project/form-designer/">form-designer 0.24</a>: Updated the package for django-recaptcha 4.0.</li> <li><a href="https://pypi.org/project/html-sanitizer/">html-sanitizer 2.3.1</a>: Fixed an edge case sanitization bug (luckily without security implications).</li> <li><a href="https://pypi.org/project/django-content-editor/">django-content-editor 6.4.2</a>: django-content-editor now again supports transitioning plugin fieldsets when opening <em>and</em> closing thanks to CSS grid&rsquo;s ability to animate the maximum height of an element. Also, the initialization in 6.4 was badly broken.</li> <li><a href="https://pypi.org/project/django-prose-editor/">django-prose-editor 0.2</a>: <a href="https://406.ch/writing/django-prose-editor-prose-editing-component-for-the-django-admin/">See the announcement blog post from Wednesday</a>.</li> </ul>Weeknotes (2024 week 07)https://406.ch/writing/weeknotes-2024-week-07/2024-02-16T12:00:00Z2024-02-16T12:00:00Z<h1 id="weeknotes-2024-week-07"><a class="toclink" href="#weeknotes-2024-week-07">Weeknotes (2024 week 07)</a></h1> <p>This is a short weeknotes entry which mainly contains a large list of releases. The reason for the large list is that I haven&rsquo;t published a weeknotes entry in weeks.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/form-designer/">form-designer 0.23</a>: Only small changes, mainly updated the package for current Django and Python versions.</li> <li><a href="https://pypi.org/project/feincms3-cookiecontrol/">feincms3-cookiecontrol 1.4.6</a>: A minor change: Swallow exceptions which happen during startup when clobbering the scripts data fails. As an aside: I find it funny that I have discovered the <code>.f3cc</code> class in some cookie banner blocklists. It feels good to be recognized even if this maybe isn&rsquo;t the nicest way, but it works for me since I actually do not like cookie banners either. At least feincms3-cookiecontrol doesn&rsquo;t inject anything without users&rsquo; consent, and doesn&rsquo;t require a third party service to run.</li> <li><a href="https://pypi.org/project/django_simple_redirects/">django-simple-redirects 2.2.0</a>: Minor release which adds a search field to the admin changelist. django-simple-redirects is a repackaged version of <code>django.contrib.redirects</code> without the <code>django.contrib.sites</code> dependency.</li> <li><a href="https://pypi.org/project/speckenv/">speckenv 6.2</a>: <code>django_cache_url</code> now supports parsing redis configuration for a leader-replica redis installation with a read-write leader host and read-only replica hosts. I use the same configuration format as <a href="https://github.com/epicserve/django-cache-url">django-cache-url</a> does.</li> <li><a href="https://pypi.org/project/django-debug-toolbar/">django-debug-toolbar 4.3</a>: I haven&rsquo;t done much here, just some reviewing here and there. I enjoy the Djangonaut Space contributions a lot.</li> <li><a href="https://pypi.org/project/django-cabinet/">django-cabinet 0.14</a>: I have removed the constraint which enforces unique names for subfolders. Enforcing the uniqueness does make sense, but it also makes bulk-updating the media library using serialized data more painful than it should be. It&rsquo;s a clear case of worse is better for me. If people want to confuse themselves I&rsquo;m not going to stop them (anymore, in this case) but it makes the rest of the code so much easier to write that it&rsquo;s not even funny.</li> <li><a href="https://pypi.org/project/html-sanitizer/">html-sanitizer 2.3</a>: This release contains a nice contribution which removes some whitespace which has been added by the sanitizer when merging adjacent tags of the same type, e.g. <code>&lt;strong&gt;abc&lt;/strong&gt;&lt;strong&gt;def&lt;/strong&gt;</code>.</li> <li><a href="https://pypi.org/project/django-ckeditor/">django-ckeditor 6.7.1</a>: See above.</li> <li><a href="https://pypi.org/project/django-json-schema-editor/">django-json-schema-editor 0.0.11</a>: Fixed a crash which happened when not providing the optional (!) configuration. Shit happens. I should really have a test suite for this package.</li> <li><a href="https://pypi.org/project/feincms3/">feincms3 4.5.2</a>: Disables the CKEditor version check.</li> <li><a href="https://pypi.org/project/django-content-editor/">django-content-editor 6.4</a>: The first release since December 2022! Very stable software. The editor now restores the collapsed state of inlines and the scroll position when using &ldquo;Save and continue editing&rdquo;. This is especially useful if editing an object with many content blocks.</li> </ul>Weeknotes (2024 week 03)https://406.ch/writing/weeknotes-2024-week-03/2024-01-17T12:00:00Z2024-01-17T12:00:00Z<h1 id="weeknotes-2024-week-03"><a class="toclink" href="#weeknotes-2024-week-03">Weeknotes (2024 week 03)</a></h1> <h2 id="djangonaut-space"><a class="toclink" href="#djangonaut-space">Djangonaut Space</a></h2> <p>I wish all participants a good time and much success. I do not have anything to do with it really but I enjoy the idea a lot and maybe there will be a pull request or two to review.</p> <h2 id="kubernetes"><a class="toclink" href="#kubernetes">Kubernetes</a></h2> <p>After years and years of hosting all sites on VPS I have finally reached the point where the old setup is more annoying to work with than switching to a new one. I have searched long for a solution which wasn&rsquo;t as limited as some PaaS and as complex as going full Kubernetes, and where I can still delegate the responsibility of actually keeping things up and running to other people. In the end I have now accepted that such a thing doesn&rsquo;t exist; either you have the limitations of a ready made solution, the limitation of having to open many many support tickets or the problem of having to learn Kubernetes (or something similar) with its extremely steep learning curve.</p> <p>After spending days with it I&rsquo;m slowly getting to the point where setting up local development environments and deploying changes is fun again. I&rsquo;m using the GitOps paradigm; while I&rsquo;m still building and uploading Docker (podman) images from the local development environment everything else is automated and goes through a Git based process. That&rsquo;s much nicer than clicking around in some interface or copy pasting obscure commands into the console.</p> <p>The biggest problem I encountered was (perhaps unsurprisingly) managing secrets as a team. It seems to me that while <a href="https://github.com/bitnami-labs/sealed-secrets/">sealed secrets</a> work great as an individual developer they don&rsquo;t really offer straightforward solutions to avoid different people overwriting and resetting each others secrets when updating them. I&rsquo;m a happy user of the external secrets operator and using some cloud service to actually store those secrets.</p> <p>I have started using <a href="https://github.com/emmett-framework/granian/">granian</a> in production. I like the idea of a Rust-based ASGI/WSGI server. Nothing mission critical yet. My idea is to build confidence in the software stack.</p> <h2 id="compulsory-social-measures"><a class="toclink" href="#compulsory-social-measures">Compulsory social measures</a></h2> <p>The more I learn about how Switzerland treats its citizens the more I wonder about the ways in which humans can mistreat other humans in a so called civilized and peaceful society.</p> <p>It&rsquo;s not exactly a new topic for me, but working on platforms which help remember and which help introducing people to the history certainly causes a heightened awareness for issues such as these.</p> <p><a href="https://www.bj.admin.ch/bj/en/home/gesellschaft/fszm.html">More on this</a>.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/feincms3-sites/">feincms3-sites 0.20.2</a>: It previously wasn&rsquo;t possible to filter the list of sites to only show those sites which do <em>not</em> have a default language. This has been fixed.</li> <li><a href="https://pypi.org/project/django-json-schema-editor/">django-json-schema-editor 0.0.10</a>: It&rsquo;s now actually possible to use the JSON editor outside inlines! That was a fun bug&hellip; not. Apart from the mentioned bug this new release mostly contains fixes to the styles. We&rsquo;re slowly getting there.</li> <li><a href="https://pypi.org/project/django-mptt/">django-mptt 0.16</a>: I didn&rsquo;t do anything except for the changelog and the release. That&rsquo;s alright. The release contains a few minor fixes.</li> </ul>Weeknotes (2024 week 01)https://406.ch/writing/weeknotes-2024-week-01/2024-01-03T12:00:00Z2024-01-03T12:00:00Z<h1 id="weeknotes-2024-week-01"><a class="toclink" href="#weeknotes-2024-week-01">Weeknotes (2024 week 01)</a></h1> <p>First weeknotes post for 2024! Happy new year!</p> <h2 id="looking-back-on-2023"><a class="toclink" href="#looking-back-on-2023">Looking back on 2023</a></h2> <h3 id="writing"><a class="toclink" href="#writing">Writing</a></h3> <p>I have published almost 40 posts last year. That&rsquo;s almost as many posts as I published in the time period from 2014 to 2023. <a href="https://jacobian.org/2021/mar/9/coworking-to-write-more/">Coworking to write more</a> does work.</p> <p>I already had a quite active blog from 2005 to 2008 with a few posts after that; everything before 2014 was in german and mainly concerned with green politics and climate change. I&rsquo;m still very interested in these topics but I don&rsquo;t feel as if I have much to add to the conversation, even though it&rsquo;s the more important issue.</p> <h3 id="open-source"><a class="toclink" href="#open-source">Open Source</a></h3> <p>Not much changed here. I enjoy basically everything I do in open source land, and co-maintaining the Django Debug Toolbar with Tim is a joy.</p> <p>I still wish that some of my projects had more impact in Django land, especially those who augment the Django administration interface to be a lightweight CMS which requires very little maintenance and work in the long run. I think it&rsquo;s great that one of the core components, <a href="https://pypi.org/project/django-content-editor/#history">django-content-editor</a>, hasn&rsquo;t required a release in more than one year. It doesn&rsquo;t have to be expanded because it just works. It would be great if there was a good way to <a href="https://406.ch/writing/managing-complexity-and-technical-debt-by-releasing-open-source-software/">distinguish</a> between software which basically doesn&rsquo;t require any updates and software which is abandoned.</p> <p>The only project which doesn&rsquo;t bring me much joy is django-mptt. Most people accept that there are no guarantees, but some people are just rude in the way in which they expect others to do the work. The first reaction was to return the blow and I&rsquo;m glad that I didn&rsquo;t give in to the temptation to do that. It&rsquo;s basically never worth it to do that in writing.</p> <h3 id="family"><a class="toclink" href="#family">Family</a></h3> <p>We had a good 2023 together and I&rsquo;m very much looking forward to a just as good 2024.</p> <h2 id="plans-for-2024"><a class="toclink" href="#plans-for-2024">Plans for 2024</a></h2> <p>I have bought a ticket for the <a href="https://2024.djangocon.eu/">DjangoCon Europe 2024</a> in Spain and I&rsquo;m very much looking forward to that.</p> <p>Maybe we&rsquo;ll visit a music festival again this summer. After listening to a lot of metal music in the last ten or more years I rediscovered the dark side of D&rsquo;n&rsquo;B. Good times.</p> <h2 id="advent-of-code"><a class="toclink" href="#advent-of-code">Advent of Code</a></h2> <p>I have finished the Advent of Code with some help from the Subreddit. Almost all of the puzzles were fun to think about. I didn&rsquo;t have fun solving each and everyone of them, especially not the second part of a few of the later days. I feel good checking out solutions from other people in the subreddit, and maybe adding newly gained ideas to my code or even running someone else&rsquo;s code 1:1 on my data after studying the algorithms used and hopefully learning something.</p> <p>I had a good time and enjoyed shutting down the computer after that.</p> <h2 id="releases"><a class="toclink" href="#releases">Releases</a></h2> <ul> <li><a href="https://pypi.org/project/FeinCMS/">FeinCMS 23.12</a>: A few minor changes to the thumbnailing code. It&rsquo;s always nice to hear from people who are still using this project.</li> </ul>