<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>shezi.de</title><link>https://shezi.de/</link><description>Just some thoughts by a guy you don't know.</description><language>en</language><lastBuildDate>Thu, 07 May 2026 08:00:00 +0200</lastBuildDate><item><title>Claude Code or How I Stopped Worrying And Love the Codepocalypse</title><link>https://shezi.de//posts/2026-04-14-claude-code.html</link><description>&lt;p&gt;I've recently started to try the new hype “agentic coding“, which is something absolutely new and not simply vibe-coding&lt;label for="mn-mistral" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-mistral" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;For some strange reason Mistral's implementation is called &lt;a href="https://github.com/mistralai/mistral-vibe"&gt;mistral-vibe&lt;/a&gt;. 🤷‍♂️&lt;/span&gt;, no no, completely new and entirely different. I've actually played around with it, using Pycharm Junie with different models, Claude Code with different models and Mistral Vibe. They're all the same, essentially, by the way, so pick whichever one you like. I even went to a workshop, organised by a client for their internal education, with two “expert trainers”, for coders and businesspeople.&lt;br /&gt;
I have learned a great many things from my experiences, and my professional opinion can be summarised like this:&lt;/p&gt;
&lt;div class="wave-text" data-text="HAVE YOU ALL COMPLETELY LOST YOUR FUCKING MINDS?"&gt;&lt;/div&gt;

&lt;p&gt;Agentic coding goes like this: You tell it what you want, it'll go away for two to 30 minutes and then it's done. Or “done”. You can influence the level of done-ness by choosing different models and different strategies, and writing different instructions into different markdown files. It's all very scientific. The one thing you must not do, however, is to look at it. No, no, never look at the code. You may check the test coverage and the greenness of the CI system, but never the code itself. Not even the plan that you have the agent make for you, or the review document. Simply press enter and be done with it.&lt;/p&gt;
&lt;p&gt;And yeah, sure, it somewhat works. Sometimes. Almost. But not really, actually.&lt;/p&gt;
&lt;p&gt;Let's say that you're a carpenter and you're producing beautiful pieces of furniture with a varied selection of tools. And then someone hands you a chainsaw. Would you go: “oh, this is so much faster than what I did before” and use &lt;em&gt;that single tool&lt;/em&gt; only for all your needs? Sure, sure, it's super quick and very easy to slice up stuff and also much less hard work than doing all the things yourself. But your furniture will be... let's say, coarse. And also not very stable. And if you want to change something, you'll just have to saw it all down and rebuild it again. And if you want to create a building with it, &lt;em&gt;I wouldn't want to live in it&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But that's how y'all are going about it. You take your hard-earned, finely-tuned and well-honed skills, sharp and precise as a scalpel and exchange them for a freaking chainsaw. And the stuff you &lt;em&gt;might&lt;/em&gt; produce &lt;em&gt;might&lt;/em&gt; look nice at first glance, and it &lt;em&gt;might&lt;/em&gt; work ok if you don't look at it too closely. And by “too closely” I mean “at all“.&lt;br /&gt;
The stuff I've seen... even the youngest learners would be &lt;em&gt;ashamed&lt;/em&gt; to hand in that stuff. But if you say “I made that with AI”, all is fine. AS IF! And AS IF you actually &lt;em&gt;had&lt;/em&gt; made it. You sat next to an idiot savant while &lt;em&gt;they&lt;/em&gt; made something that vaguely looks like the thing you described, and you probably twiddled your thumbs and wasted your life at the same time, too.&lt;/p&gt;
&lt;p&gt;Here's the thing: I can produce good software, and I can produce bad software&lt;label for="mn-software" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-software" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;And I specifically say “software” here. This goes beyond mere code, since it includes architecture, structure, integration, and also taste, which is the most important thing when designing APIs.&lt;/span&gt;. And the difference isn't super-easy to discern, if you're not a producer of software, which has been a problem for ages. “But you're almost done” is what your PM would say, because they didn't know better. And you had to educate them and push back and explain about the first 90% and the second 90%, but also  about &lt;a href="https://philosophistry.com/archives/2009/01/what-is-a-black.html"&gt;black triangles&lt;/a&gt; and about Mythical Man Months . But all that is gone, because now you're stuck in eternal almost-done, and&lt;/p&gt;
&lt;div class="wave-text" data-text="everyone gets to make bad software now."&gt;&lt;/div&gt;

&lt;p&gt;Congratulations, because that's exactly what the industry and the world needed: more bad software.&lt;/p&gt;
&lt;h2&gt;And that's not even the worst part!&lt;/h2&gt;
&lt;p&gt;But it gets worse, because you give these tools to amateurs. To the PMs and the POs and the business analysts, so &lt;em&gt;they&lt;/em&gt; get to make bad software now. And they &lt;em&gt;can't&lt;/em&gt; understand the difference, until it breaks and takes your carefully balanced systems down. But don't worry, because cLaUdE cAn FiX iT!&lt;br /&gt;
I would absolutely give a chainsaw to a beginner on their first day, sure!&lt;/p&gt;
&lt;p&gt;I've seen people try to copy their error messages into their vibe coding interface, and when that didn't help, they tried again&lt;label for="mn-onus" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-onus" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Ok, that one's on us, we trained people to just try it again.&lt;/span&gt;. And when &lt;em&gt;that&lt;/em&gt; didn't help, they installed a Playwright MCP and let the statistical parrot &lt;em&gt;control their main web browser&lt;/em&gt;. The one that has all their logins and passwords and access to the internet, you know. Working software developers helped them do this!&lt;br /&gt;
WHAT THE ACTUAL FUCK? Yes, it certainly is a good idea to add some nitro to the tank of the chainsaw and also don't wear any protective clothing and also just put it on this bag of TNT when you start it up!&lt;/p&gt;
&lt;p&gt;And now I hear people talking about the stupidest ideas like: “What can you even tell young people to learn? We won't need developers or physicists or lawyers or actors or musicians any more &lt;em&gt;because ChatGPT can do all that&lt;/em&gt;.” These people are so mislead that they will endanger &lt;em&gt;their children's futures&lt;/em&gt; for pure laziness. They've already lost so much mental capacity to the machines that they can't even think about what a &lt;em&gt;full person&lt;/em&gt; could do in their life. It's so sad and stupid and also all kinds of disgusting.&lt;/p&gt;
&lt;h2&gt;But wait, the worst part is this!&lt;/h2&gt;
&lt;p&gt;To my fellow software engineers I say this: HAVE YOU NO FUCKING SHAME? Are you not ashamed to be associated with this slop, this drivel, this entire fucking mess? Are you so blinded by the allure of laziness, of not having to do the thinking yourself that you go along with this shit!&lt;/p&gt;
&lt;p&gt;I've seen guys vibe-code some feature for their website and deploy it without looking at the code even once. Not even a glance. I am so freaking astonished that anyone calling themselves a professional would sink that low.&lt;/p&gt;
&lt;p&gt;Actually no, it's not that anyone would &lt;em&gt;do&lt;/em&gt; that. It's the &lt;em&gt;speed&lt;/em&gt; with which our profession abandoned all pretense of precision or accuracy or dignity or professionalism.&lt;/p&gt;
&lt;p&gt;You're addicts, go get help!&lt;/p&gt;
&lt;h2&gt;A tool&lt;/h2&gt;
&lt;p&gt;A chainsaw is a tool&lt;label for="mn-tool" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-tool" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Either &lt;em&gt;it&lt;/em&gt; is the tool or you are. Your choice.&lt;/span&gt;, and, for the right thing, you get to use it. Carefully, and with deliberation and respect. You get to chop up stuff with it, and then you take the stuff and &lt;em&gt;build&lt;/em&gt; something out of it.&lt;/p&gt;
&lt;p&gt;I'm not going to pretend that AI and vibe coding is &lt;em&gt;not&lt;/em&gt; an amazingly powerful tool. I'm not going to pretend that I'm not using it, or that I'll stop using it.&lt;/p&gt;
&lt;p&gt;But &lt;em&gt;I know what I'm doing&lt;/em&gt; and I'm not going to blindly apply it to everything. I'm going to have architecture in my software, because that's whats going to make it usable ten minutes from now, when you can't even remember which framework you're using because &lt;em&gt;you never bothered to check&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Someone's going to need to help you when you finally find out the limits of your vibe coding agent. When your carefully constructed building comes crashing down because no one understands what it's even made out of; not your agent and certainly not you – you will need professional help. This is something &lt;em&gt;I&lt;/em&gt; can do. Because I spent the time and learned all that stuff, and &lt;em&gt;didn't&lt;/em&gt; spend my time sitting next to a box that pretends to do it for me.&lt;/p&gt;
&lt;p&gt;So if you find yourself in that situation, you may talk to me about it.  &lt;/p&gt;
&lt;div style="font-size: 45%; padding-top: 10em;"&gt;None of the words in this essay were written by an AI. They were all written by RI, real intelligence.&lt;/small&gt;

&lt;!-- the following part was written with Claude Code. Actually, I tried Mistral first, but that failed. Claude was better,
but not much. In the time I used to explain what I wanted to the machine and made adjustments, I COULD HAVE LEARNED
TO DO IT MYSELF, and do it better, too. --&gt;
&lt;style&gt;
  /* ── Wave text base styles ─────────────────────────── */

  .wave-text {
    margin: 2rem 0;
  }

  .wave-text .space {
    display: inline-block;
    width: 0.35em;
  }

  .wave-text .word {
    display: inline-flex;
    white-space: nowrap;
  }

  .wave-text .char {
    display: inline-block;
    font-weight: 600;
    color: #e8e4f0;
    will-change: transform, color;
  }
  /* Font size defaults — override with size-sm / size-lg class */
  .wave-text          .char,
  .wave-text          .space { font-size: 1.4rem; }

&lt;/style&gt;


&lt;script&gt;
  // ── DEFAULT CONFIG ─────────────────────────────────────
  // Each .wave-text element can override these via data attributes:
  //   data-speed="1.3"    → wave action duration (seconds)
  //   data-pause="6"      → idle gap between waves (seconds)
  //   data-stagger="0.07" → delay between characters (seconds)
  // ───────────────────────────────────────────────────────
  const DEFAULTS = {
    speed:   0.7,
    pause:   4,
    stagger: 0.02,
  };

  // Track which keyframe variants we've already injected
  const injectedKeyframes = {};

  function safeId(speed, pause) {
    // Replace dots with underscores so the name is a valid CSS identifier
    return `wave_${String(speed).replace('.', '_')}__${String(pause).replace('.', '_')}`;
  }

  function buildKeyframes(speed, pause) {
    const total = speed + pause;
    const name = safeId(speed, pause);

    if (injectedKeyframes[name]) return { name, total };
    injectedKeyframes[name] = true;

    const pct = (sec) =&gt; ((sec / total) * 100).toFixed(2);
    const stops = [0, 0.19, 0.38, 0.56, 0.75, 1].map(s =&gt; pct(s * speed));

    const css = `
      @keyframes ${name} {
        0%, ${stops[0]}% {
          transform: scaleX(1) scaleY(1) translateY(0);
          color: #111;
        }
        ${stops[1]}% {
          transform: scaleX(1.12) scaleY(1.55) translateY(-12%);
          color: #ff4d9a;
        }
        ${stops[2]}% {
          transform: scaleX(1.08) scaleY(1.3) translateY(-6%);
          color: #ffb830;
        }
        ${stops[3]}% {
          transform: scaleX(1.04) scaleY(1.18) translateY(-2%);
          color: #30e8aa;
        }
        ${stops[4]}% {
          transform: scaleX(1.01) scaleY(1.02) translateY(0);
          color: #5090ff;
        }
        ${stops[5]}%, 100% {
          transform: scaleX(1) scaleY(1) translateY(0);
          color: #111;
        }
      }
    `;

    const style = document.createElement('style');
    style.textContent = css;
    document.head.appendChild(style);

    return { name, total };
  }

  function initWaveText(el) {
    const speed   = parseFloat(el.dataset.speed)   || DEFAULTS.speed;
    const pause   = parseFloat(el.dataset.pause)   || DEFAULTS.pause;
    const stagger = parseFloat(el.dataset.stagger) || DEFAULTS.stagger;

    const { name: animName, total } = buildKeyframes(speed, pause);

    const text = el.dataset.text;
    const tokens = text.split(/(\s+)/);
    const totalChars = text.replace(/\s/g, '').length;
    let charIndex = 0;

    tokens.forEach(token =&gt; {
      if (/^\s+$/.test(token)) {
        for (let i = 0; i &lt; token.length; i++) {
          const spacer = document.createElement('span');
          spacer.className = 'space';
          el.appendChild(spacer);
        }
        return;
      }

      const wordEl = document.createElement('span');
      wordEl.className = 'word';

      [...token].forEach(letter =&gt; {
        const span = document.createElement('span');
        span.className = 'char';
        span.textContent = letter;
        const delay = (totalChars - 1 - charIndex) * stagger;
        span.style.animation = `${animName} ${total}s cubic-bezier(0.36, 0, 0.64, 1) infinite`;
        span.style.animationDelay = `${delay.toFixed(2)}s`;
        wordEl.appendChild(span);
        charIndex++;
      });

      el.appendChild(wordEl);
    });
  }

  // Initialize every .wave-text on the page
  document.querySelectorAll('.wave-text').forEach(initWaveText);
&lt;/script&gt;</description><pubDate>Thu, 07 May 2026 08:00:00 +0200</pubDate></item><item><title>To-Read: the books I plan on reading some day</title><link>https://shezi.de//posts/2025-06-27-toread.html</link><description>&lt;p&gt;This list will grow and shrink. Mostly grow.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.m.wikipedia.org/wiki/War_with_the_Newts"&gt;War with the Newts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bookshop.org/p/books/monk-and-robot-becky-chambers/21752517?ean=9781250386335&amp;amp;next=t&amp;amp;affiliate=6134"&gt;Monk and Robot by Becky Chambers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bookshop.org/p/books/atacama-jendia-gammon/SXrwRCVNVqg74LQa?ean=9781957941165&amp;amp;next=t&amp;amp;affiliate=6134"&gt;Atacama by Jendia Gammon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bookshop.org/p/books/the-incandescent-emily-tesh/21751522?ean=9781250835017&amp;amp;next=t&amp;amp;affiliate=6134"&gt;The Incandescent by Emily Tesh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A Rebel's History of Mars by Nadia Afifi&lt;/li&gt;
&lt;li&gt;Shroud by Adrian Tchaikovsky&lt;/li&gt;
&lt;li&gt;Of Monsters and Mainframes by Barbara Truelove&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;</description><pubDate>Fri, 27 Jun 2025 08:00:00 +0200</pubDate></item><item><title>DjangoconEU2025: Talk recommendations</title><link>https://shezi.de//posts/2025-06-05-djangocon-talks.html</link><description>&lt;p&gt;It looks like &lt;a href="https://www.youtube.com/@DjangoConEurope/videos"&gt;the DjangoconEU2025 talks are starting to appear on their youtube channel&lt;/a&gt;. Cool!&lt;/p&gt;
&lt;p&gt;Of course, all the talks are great and you could, should watch all of them. What follows is not a judgement on any of the talks, especially not on the talks that I'll skip. However, some of the talks are more equal than others, and play more to my taste, so here is a list of things that I found interesting:&lt;/p&gt;
&lt;h2&gt;Day 1&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=kZ4q0k_FNhw"&gt;Tim Bell: Turn back time: Converting integer fields to bigint using Django migrations at scale&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/DLUM9M/"&gt;conference page&lt;/a&gt;): The harrowing tale of what lengths you need to go to if you &lt;em&gt;forgot&lt;/em&gt; to start out with BigInt fields as primary keys, and not expecting to ever have database tables with 2 billion rows.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=L7WXcWuH21U"&gt;Adam Johnson: Data-Oriented Django Drei&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/KKABLJ/"&gt;conference page&lt;/a&gt;): More about database indexes and how to use them in smart ways.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=r5c_gxuMcL4"&gt;Sebastian Rode: The fine print in Django release notes&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/ATA3HV/"&gt;conference page&lt;/a&gt;): You can find the most interesting things by simply reading the Django release notes.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=lYZHDP0j1Qw"&gt;Aivars Kalvāns: How to solve a Python mystery&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/PGQKUW/"&gt;conference page&lt;/a&gt;): ... by diving deep into tracing the Linux system. And this was billed as &lt;strong&gt;Audience Level:&lt;/strong&gt; Intermediate.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=PV7JFQPYQyI"&gt;Slawa Gladkov: Logs, shells, caches and other strange words we use daily&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/9QPXCZ/"&gt;conference page&lt;/a&gt;): A bit less technical but a bit more entertaining, and definitely worth watching -- even if you knew all of these terms before, of course you did!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=ro_LaEe9w1A"&gt;Lightning Talks&lt;/a&gt;: Always good! Watch at least the ones on “Caddy snake“ (which led to heated discussions on the bus later), and the one on “safer migrations”.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Day 2&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=PBbM9pH8rQY"&gt;Mia Bajić: The Most Bizarre Software Bugs in History&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/W3BQVT/"&gt;conference page&lt;/a&gt;): very entertaining and thought-provoking.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=TUv9u8Uc_uk"&gt;Wouter Steenstra: 100 Million Parking Transactions Per Year with Django&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/YHVBFJ/"&gt;conference page&lt;/a&gt;): finally some real-world data on how far you can go with a Django app, and what tricks you could/should employ&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=l1xi_yKnhbE"&gt;Haki Benita: How to get Foreign Keys horribly wrong in Django&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/HAFUBH/"&gt;conference page&lt;/a&gt;): Fantastic talk on the database and migrations and no, I did not know that NULL keys would be indexed!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=wY7dpWmUQAY"&gt;Jacob Walls: Dynamic models without dynamic models&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/DGQ9JD/"&gt;conference page&lt;/a&gt;): just the &lt;em&gt;absolute madness&lt;/em&gt; of this scheme makes it worth watching. If you ever do something like this in production we need to have a serious talk!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/80RKCH2PmEU"&gt;Lightning talks&lt;/a&gt;: Always good! Especially the one on &lt;a href="https://youtu.be/80RKCH2PmEU?t=1315"&gt;how to fall asleep within five minutes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Day 3&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=gt19rN6HS6M"&gt;Graham Knapp: Feature Flags: Deploy to some of the people all of the time, and all of the people some of the time!&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/KNLFS8/"&gt;conference page&lt;/a&gt;): I've always wanted to do feature flags, and this here is one way of doing it. &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=lL2RtVlaafQ"&gt;Vince Salvino: One Thousand and One Django Sites&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/LPUSJR/"&gt;conference page&lt;/a&gt;): how to host a thousand pages (and more)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=jrDCmuD9cBc"&gt;Agnès Haasser: Europe, Django and two-factor authentication&lt;/a&gt; (&lt;a href="https://pretalx.evolutio.pt/djangocon-europe-2025/talk/GABN73/"&gt;conference page&lt;/a&gt;): The challenges of implementing and shipping MFA tokens to your users. And also, you should use MFA tokens!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=Nim1zHomTU0"&gt;Lightning talks&lt;/a&gt;: always good!&lt;/li&gt;
&lt;/ul&gt;</description><pubDate>Thu, 05 Jun 2025 08:00:00 +0200</pubDate></item><item><title>Rounding properly in Python</title><link>https://shezi.de//posts/2025-05-11-rounding.html</link><description>&lt;p&gt;If you want to see code, skip down to &lt;a href="#the-solution"&gt;the solution&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some time ago, Python changed its rounding algorithm&lt;label for="mn-sometime" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-sometime" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;And by “some time ago“ I mean 17 years at the time of writing&lt;/span&gt;. Instead of always rounding &lt;em&gt;up&lt;/em&gt; at 0.5 values, it would round &lt;em&gt;towards the even number&lt;/em&gt;, so 0.5 would be rounded down to 0, while 1.5 would be rounded up to 2:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The reasoning behind this is that it's conforming to &lt;a href="https://en.wikipedia.org/wiki/IEEE_754#Rounding_rules"&gt;the IEEE754 standard on rounding numbers&lt;/a&gt;, or, well, at least the specific default rule of it. The better reason is that it takes away an &lt;a href="https://en.wikipedia.org/wiki/Rounding#Rounding_to_the_nearest_integer"&gt;aspect of rounding bias&lt;/a&gt;. The method called “round half to even” is unbiased both on positive/negative numbers and towards/away from zero or infinity. Mathematically, it's nice.&lt;/p&gt;
&lt;p&gt;It's just not what most people expect when rounding numbers, what they expect is “rounding as we learned it in school”, also called “commercial rounding” or “round half away from zero”. And that's what I'd like to have. Well, as one commenter put it, &lt;a href="https://stackoverflow.com/questions/18473563/python-incorrect-rounding-with-floating-point-numbers/18473727#18473727"&gt;simply implement it by hand&lt;/a&gt;. Ah yes, that's exactly what I had intended for a quiet evening, re-implementing basic math functions that should be available everywhere.&lt;/p&gt;
&lt;p&gt;Unfortunately, things aren't that simple, and for this specific operation, we cannot stay with floating point numbers, since they are encoded in binary and mixing terms like “round to the nearest decimal digit” really don't make sense in that context. So we'll have to switch to &lt;code&gt;Decimal&lt;/code&gt;s, because those &lt;em&gt;can&lt;/em&gt; properly encode decimal numbers.&lt;/p&gt;
&lt;p&gt;Unfortunately, it's not that easy either. Reading through &lt;a href="https://docs.python.org/3/library/decimal.html"&gt;the documentation in the standard library on Decimals&lt;/a&gt;, you might come away with this idea:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;decimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getcontext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0.142857&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getcontext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0.1428571428571428571428571429&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And you'll think to that you'll simply get the current context, set its precision to 3 and be done. But unfortunately:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getcontext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0.12345&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0.12345&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0.12345&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# ok, we have to force a precision adjustment, 𝓯𝓲𝓷𝓮&lt;/span&gt;
&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0.123&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0.1235&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0.124&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# nice!&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# ... some time later&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;12345&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1.23E+4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# WTF?!&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;12345&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1.24E+4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;12355&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1.24E+4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# WT actual F?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You see, that beautiful &lt;code&gt;prec&lt;/code&gt; parameter sets the &lt;em&gt;precision&lt;/em&gt; of the decimal type and is set to 28 by default. It specifies how many decimal digits a number carries &lt;em&gt;in total&lt;/em&gt;. So the result of &lt;code&gt;12345 + 1 --&amp;gt; 12300&lt;/code&gt;. Also, per the last line, rounding towards even is still active. &lt;em&gt;Nice!&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;Things I tried out&lt;/h1&gt;
&lt;p&gt;Here are six variations of how to do proper rounding that I thought up from reading the documentation. Not all of these are correct. Try to guess which ones work!&lt;/p&gt;
&lt;h2&gt;Variant 1&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;getcontext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;
&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Variant 2&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Variant 3&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Variant 4&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Variant 5&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;localcontext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Variant 6&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;localcontext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;Solutions&lt;/h1&gt;
&lt;h2&gt;Variant 1&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;getcontext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;
&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This one &lt;em&gt;works&lt;/em&gt;, &lt;strong&gt;but&lt;/strong&gt; it sets the rounding mode &lt;em&gt;globally&lt;/em&gt;, for all decimal processing in your program. Depending on your needs, this might be what you want. Set your rounding mode once, be done.&lt;/p&gt;
&lt;h2&gt;Variant 2&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This one &lt;strong&gt;&lt;em&gt;does not work&lt;/em&gt;&lt;/strong&gt;. The function &lt;code&gt;create_decimal&lt;/code&gt; only does its thing once, the created number does not have an association with the context.&lt;/p&gt;
&lt;h2&gt;Variant 3&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This one also &lt;strong&gt;&lt;em&gt;does not work&lt;/em&gt;&lt;/strong&gt;. The function &lt;code&gt;create_decimal&lt;/code&gt; still only does its thing once, the created number does not have an association with the context, even if you use fancy words like &lt;code&gt;quantize&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Variant 4&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This one &lt;em&gt;works&lt;/em&gt;, and it's actually my preferred solution, because we don't have to change any global state. We simply apply the context to the operation, even if it is through unnecessarily complex verbiage.&lt;/p&gt;
&lt;p&gt;But beware of that second parameter. It does &lt;em&gt;not&lt;/em&gt; refer to the number of digits, instead it refers to the exponent. If you want to round to one digit, that parameter needs to be &lt;code&gt;Decimal('0.1')&lt;/code&gt; or &lt;code&gt;Decimal('1e-1')&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Variant 5&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;localcontext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This one &lt;em&gt;works&lt;/em&gt;. Its effects are localised to a specific context and will reset it back afterwards. Still uses fancy words, though!&lt;/p&gt;
&lt;h2&gt;Variant 6&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;localcontext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Nice, clean, localised, readable, and &lt;em&gt;works&lt;/em&gt;. I like it. But not as much as the other one.&lt;/p&gt;
&lt;h1&gt;Rounding modes&lt;/h1&gt;
&lt;style&gt;table tr:nth-child(even) {background-color:#f0f0f0;}&lt;/style&gt;

&lt;p&gt;You can &lt;a href="https://docs.python.org/3/library/decimal.html#rounding-modes"&gt;read up on the available rounding modes&lt;/a&gt;, or you can use the little table I made. &lt;a href="https://gitlab.com/-/snippets/4842336"&gt;You can also check out the code I used to make this table&lt;/a&gt;, just in case you don't trust me.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;0.4&lt;/th&gt;
&lt;th&gt;0.5&lt;/th&gt;
&lt;th&gt;0.6&lt;/th&gt;
&lt;th&gt;1.4&lt;/th&gt;
&lt;th&gt;1.5&lt;/th&gt;
&lt;th&gt;1.6&lt;/th&gt;
&lt;th&gt;-0.4&lt;/th&gt;
&lt;th&gt;-0.5&lt;/th&gt;
&lt;th&gt;-0.6&lt;/th&gt;
&lt;th&gt;-1.4&lt;/th&gt;
&lt;th&gt;-1.5&lt;/th&gt;
&lt;th&gt;-1.6&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ROUND_CEILING&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROUND_DOWN&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROUND_FLOOR&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROUND_HALF_DOWN&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROUND_HALF_EVEN&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROUND_HALF_UP&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;-0&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROUND_UP&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROUND_05UP&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;No, I have no idea why &lt;code&gt;ROUND_05UP&lt;/code&gt; would be a good idea.&lt;/p&gt;
&lt;h1&gt;The Solution&lt;/h1&gt;
&lt;p&gt;&lt;a id="the-solution"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So, here is how you round like you learnt in school, localised to a specific context:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;decimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROUND_HALF_UP&lt;/span&gt;

&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ROUND_HALF_UP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;rounding_exponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1E-4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_decimal_from_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rounding_exponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Change the &lt;code&gt;4&lt;/code&gt; to however many digits you need!&lt;/p&gt;</description><pubDate>Sun, 11 May 2025 08:00:00 +0200</pubDate></item><item><title>CSV sucks</title><link>https://shezi.de//posts/2025-03-27-csv-sucks.html</link><description>&lt;p&gt;I've said that CSV sucks many times.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=eEZYDDaDeCs&amp;amp;t=1163s"&gt;I am on record saying publicly that CSV sucks.&lt;/a&gt; That video is a lightning talk that I held at Djangocon 2018 in Darmstadt, but I've said that bit before and after.&lt;/p&gt;
&lt;p&gt;I advise against using CSV in any situation where you can avoid it. It is a bad format, it has bad usability and you shouldn't use it.&lt;/p&gt;
&lt;p&gt;CSV is also not “dead” in any sense of the word. In fact, I would say it is &lt;em&gt;undead&lt;/em&gt;, animated by the will of mad wizards, against its will, and it should have died a long time ago.&lt;br /&gt;
From time to time, I read from &lt;em&gt;proponents&lt;/em&gt; of the CSV format, and most of the time, that's ok. You do you! Stay stuck in the stupid timeline. But sometimes, they just rub me the wrong way. &lt;a href="https://github.com/medialab/xan/blob/master/docs/LOVE_LETTER.md"&gt;Like it happened yesterday on the front page of HN.&lt;/a&gt; Those arguments come from an especially weird place, because they compare CSV to parquet, JSONL, MessagePack and the like, most of which have very different use cases. If you start out with a format that doesn't fit your use case, you're really quite doomed from the start&lt;label for="mn-jsonl" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-jsonl" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;I'd really like to see you encode OpenTelemetry data with nested spans and added records into a CSV file in a sane way.&lt;/span&gt;. If the format you choose happens to be a bad format (like CSV), you are beyond help.&lt;/p&gt;
&lt;p&gt;Before we get to why CSV is really really actually quite really bad (it will shock you!), let me go through the presented arguments real quick.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;1. CSV is dead simple&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes, sure, except when it isn't. If everyone could just agree on what CSV actually &lt;em&gt;is&lt;/em&gt;, then, sure, we &lt;em&gt;might&lt;/em&gt; say that it's simple. Still not as simple as &lt;em&gt;the proper solution&lt;/em&gt; which I will present to you at the end, but ok, simple enough, I guess. Alas, in the real world it's about as simple as travelling to another country and plugging in your electrical appliances there. Except at least the different plug standards are at least not ALL called the same thing.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://docs.python.org/3/library/csv.html"&gt;the documentation of the CSV reader library in Python&lt;/a&gt;. If you think that one counts as “simple”, well, you must be way smarter than me&lt;label for="mn-fair" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-fair" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;To be fair, that is possible. Probable even. Alright, alright, you probably &lt;em&gt;are&lt;/em&gt; much smarter than me!&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;By the way, so is JSONL.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;2. CSV is a collective idea&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;No, it's a collective &lt;em&gt;bad idea&lt;/em&gt;. There are much better alternatives (see below) that are &lt;em&gt;well-thought-out&lt;/em&gt;, a property that collective ideas famously lack.&lt;/p&gt;
&lt;p&gt;To me, that argument boils down to: “millions of raccoons eat garbage, so eat more garbage!”&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;3. CSV is text&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes, and that comes with advantages and disadvantages. So let's count that as a neutral point, and &lt;strong&gt;compare it with some alternatives&lt;/strong&gt; below. By the way, so is JSONL.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;4. CSV is streamable&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, yes, maybe. If that's your use case, fantastic. There are still better alternatives, below. By the way, so is JSONL.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;5. CSV can be appended to&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;On the off chance that I may be repeating myself, so are most of the alternatives below. By the way, so is JSONL.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;6. CSV is dynamically typed&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am a fan and proponent of dynamic types, and I rather dislike statically typed programs&lt;label for="mn-qualify" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-qualify" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;in most situations, where appropriate, depending on use case, etc. etm.&lt;/span&gt;, &lt;strong&gt;but&lt;/strong&gt; for interfaces, I think you should have the strictest contracts you can make, and make very damn sure that the provider of the interface sticks to the contract. That's the reasoning behind &lt;em&gt;all&lt;/em&gt; of the interface description languages, from the most ancient WSDL down to the most modern &lt;a href="https://typespec.io/blog/2024-04-25-introducing/"&gt;TypeSpec&lt;/a&gt;&lt;label for="mn-name" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-name" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;IT HAS IT RIGHT IN THE NAME!&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;For an interface, you must make sure that what you get is what you expect.&lt;/p&gt;
&lt;p&gt;Guess how many guarantees CSV gives you? &lt;strong&gt;Zero!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There is a beautiful story where &lt;a href="https://csvbase.com/blog/5"&gt;you can download the historic Euro exchange rates from the ECB as a CSV file&lt;/a&gt;&lt;label for="mn-1" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-1" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Note that the first thing this article from CSVbase (of all places) does is convert it to a proper data format.&lt;/span&gt;, but they mistakenly added a comma at the end of the row, and now they will have to keep that useless comma forever because it might break legacy customers' parsing, all thanks to an under-specified format language.&lt;/p&gt;
&lt;p&gt;By the way, so is JSONL.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;7. CSV is succinct&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sure, compared to formats where you can have differently-structured objects in each line where you &lt;em&gt;must&lt;/em&gt; repeat the keys (again, use case!), CSV is slightly more succinct. Compared to other &lt;em&gt;tabular&lt;/em&gt; formats, it is not.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Of course, statically-typed numbers could be represented more concisely, but you will not save up an order of magnitude there neither.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Go download that ECB file from above and say that again!&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;8. Reverse CSV is still valid CSV&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;This must be the weirdest argument in the bunch. You can hack your way to reading from the end of a CSV file by “feed the bytes of your file in reverse order to a CSV parser, then reverse the yielded rows and their cells' bytes and you are done (maybe read the header row before though)”?!? And that is supposed to be simpler than scanning for newlines (or any record separator) from the end of the file?! Or doing an index scan in any of the grown-up formats? I beg your hecking pardon?&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;9. Excel hates CSV&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;The argument of “interoperation with the most widely-used tool is possible, but difficult, and may break in unexpected ways” is not the own you think it is.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Signed: &lt;a href="https://github.com/medialab/xan#readme"&gt;xan&lt;/a&gt;, the CSV magician&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Look, this is really what it boils down to. &lt;em&gt;I don't want a magician of a data interchange format.&lt;/em&gt; I want an interchange format to be well-defined, well-supported by libraries and languages and without surprises nor Stockholm syndrome. I don't want magic and wizardly knowledge just to transfer some rows of data.&lt;/p&gt;
&lt;h1&gt;The worst thing about it&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;It was worse than a crime; it was a blunder.&lt;br /&gt;
- &lt;a href="https://en.wikipedia.org/wiki/Louis_Antoine,_Duke_of_Enghien"&gt;Louis Antoine, Duke of Enghien&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The worst thing about CSV is that it's a hack, and a bad one at that. All of the format difficulties stem from the single fact that we use characters that can appear in the data as structural elements. That means that you must escape strings, you must think about &lt;em&gt;which&lt;/em&gt; characters to use as separators (both for fields as well as records) and you have to deal with all the things that can/must occur when someone writes wrongly, because you can't detect it right away, or at all.&lt;br /&gt;
Using the same characters in data as in the format &lt;strong&gt;is a hack&lt;/strong&gt;. It's the first thing you think of because it's simple and stupid, and it leads to all kinds of problems down the line. &lt;br /&gt;
Fortunately, smarter minds have thought about this problem before and have found a much better solution: simply use different characters. Sadly, almost no-one knows about that. Here's an excerpt from the ASCII standard: &lt;/p&gt;
&lt;figure class="fullwidth"&gt;
  &lt;img srcset="/_images/posts/ascii-excerpt-2332.png 2332w,/_images/posts/ascii-excerpt-1166.png 1166w,/_images/posts/ascii-excerpt-583.png 583w,/_images/posts/ascii-excerpt-291.png 291w" src="/_images/posts/ascii-excerpt-291.png" alt="Excerpt from the ASCII standard, showing different separator characters used for separating units, groups, records and files"&gt;
  &lt;small&gt;&lt;a href="https://en.wikipedia.org/wiki/C0_and_C1_control_codes#Field_separators"&gt;from Wikipedia: https://en.wikipedia.org/wiki/C0_and_C1_control_codes&lt;/a&gt;&lt;/small&gt;
&lt;/figure&gt;

&lt;p&gt;Here's all the code you need to parse an ASV file, with ascii separated values, in Python:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;file.asv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\x30&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\x31&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No &lt;code&gt;if&lt;/code&gt;s, &lt;code&gt;and&lt;/code&gt;s or &lt;code&gt;but&lt;/code&gt;s&lt;label for="mn-writer" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-writer" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;The code for the writer is equally simple and left as an exercise for the reader.&lt;/span&gt;. That's all, unambiguously. No escaping, no faffing, no nothing. You want to have different data in a single file? Go use the Group Separator and File Separator codes and just stuff it all in there! And done.&lt;/p&gt;
&lt;p&gt;If only they'd read the standard.&lt;/p&gt;
&lt;p&gt;If only they had just taken just a little time to just think about it instead of going right ahead, we'd have been spared this silly mess. But they didn't just, they won't just, and no one in the history of absolutely everything ever has just.&lt;/p&gt;
&lt;h1&gt;Alternatives&lt;/h1&gt;
&lt;p&gt;Alright, enough whining, time for some positive outlook.&lt;/p&gt;
&lt;p&gt;Let's only look at the use-case of tabular data with relatively unchanging formats, the occasional empty field&lt;label for="mn-null" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-null" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;How do you code an empty field in CSV?! Do you leave it empty? Is that an empty string right in the middle of your beautiful data? Or is it a null? WHO FLIPPING KNOWS?!&lt;/span&gt; notwithstanding. Choose differently for different use-cases: parquet or other columnar data files are for columnar data, JSONL is for varying structures in every line!&lt;/p&gt;
&lt;h2&gt;ASV&lt;/h2&gt;
&lt;p&gt;If you have the control, go for ASV! It has all the advantages of CSV, plus none of the silliness. The only bad thing is that editors don't properly show the separator codes (for viewing, a search-and-replace with newline and tab will be sufficient), nor is it super-easy to type them. &lt;/p&gt;
&lt;h2&gt;SQLite&lt;/h2&gt;
&lt;p&gt;An even better choice would be SQLite! I mean, if you don't need to rear-feed your files and then invert them, but actually no, even then, because &lt;a href="https://notes.billmill.org/databases/sqlite/Statically_hosted_sqlite_with_range_queries.html"&gt;you can do that with range queries&lt;/a&gt;. Sure, sure, you can't open it with every text editor, but the tooling is portable and available almost everywhere&lt;label for="mn-pysqlite" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-pysqlite" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;If you have python, you can open any SQLite file.&lt;/span&gt;; and sure, sure, you need to learn all of these two SQL commands: &lt;code&gt;select * from table;&lt;/code&gt; and &lt;code&gt;insert into table values (...)&lt;/code&gt;. But you'll manage, and for these herculean efforts you get proper data integrity, proper data types (if you want, SQLite can be flexible if you need it to be), multiple tables in one file, proper binary packing and random access scanning and full SQL querying.&lt;/p&gt;
&lt;h2&gt;Protobuf&lt;/h2&gt;
&lt;p&gt;On the simpler, more machine-oriented end of the range, there's &lt;a href="https://en.wikipedia.org/wiki/Protocol_Buffers"&gt;Protobuf&lt;/a&gt;. If you have more than a few fields, the gains from not storing numbers as text is massive! For transferring some data between API endpoints, Protobuf is a great choice!&lt;/p&gt;
&lt;p&gt;You can download &lt;a href="https://github.com/pyrosm/pyrosm"&gt;OpenStreetmap data in Protobuf format&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;Don't delude yourself!&lt;/p&gt;
&lt;p&gt;CSV bad.&lt;/p&gt;
&lt;p&gt;Other formats better.&lt;/p&gt;
&lt;p&gt;Choose wisely!&lt;/p&gt;</description><pubDate>Thu, 27 Mar 2025 08:00:00 +0100</pubDate></item><item><title>Die Wahl</title><link>https://shezi.de//posts/2025-01-30-die-wahl.html</link><description>&lt;p&gt;Jetzt ist es also bald Zeit für die Wahl. Es wird wohl eine der wichtigsten meines (zumindest bisherigen) Lebens werden. Schon witzelt der Postillion &lt;a href="https://www.der-postillon.com/2024/09/aufstieg-des-nationalsozialismus.html"&gt;dass sich die Historiker freuen, live zu sehen wie es damals begann&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ich hatte viele Worte hier geschrieben und sie dann doch wieder gelöscht, denn eigentlich bleibt nur eins zu sagen:&lt;/p&gt;
&lt;p&gt;Wer CDU oder AFD wählt, ist mit Schuld daran, dass Deutschland und die Welt schlechter wird. Und mit solchen Menschen will ich nichts zu tun haben.&lt;/p&gt;</description><pubDate>Thu, 30 Jan 2025 19:15:00 +0100</pubDate></item><item><title>Static Full Text Search</title><link>https://shezi.de//posts/2025-01-10-static-full-text-search.html</link><description>&lt;p&gt;I've decided that what this site needs is full text search. It really is essential for navigating all my exhilarating content! &lt;a href="/search.html"&gt;Check it out on the search page!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But I also don't want to move away from static hosting. I don't want to care for a search backend and data ingestion and all the stuff that comes from hosting a site. Not that I &lt;em&gt;couldn't&lt;/em&gt; do it, and in a &lt;em&gt;professional&lt;/em&gt; context with a site that has enough data, that is absolutely what I'd suggest&lt;label for="mn-hire" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-hire" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;And in case you need something along those lines you should definitely &lt;a href="mailto:j@spielmannsolutions.com"&gt;hire me!&lt;/a&gt;&lt;/span&gt;, but for my small little personal site it sounds like too much of a hassle.&lt;br /&gt;
So I built a static search for this site, using my site generator &lt;a href="https://gitlab.com/jspielmann/heron/"&gt;Heron&lt;/a&gt; and the JS search library &lt;a href="https://www.fusejs.io/"&gt;Fuse.js&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you're only interested in learning how to do this/implementing it yourself, you can skip to the &lt;a href="#tutorial"&gt;tutorial part&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Heron&lt;/h2&gt;
&lt;p&gt;It really is quite simple. I added a transformer that takes the root collection (ie. all files), pulls out the original markdown and sticks it into a single file as the simplest JSON I could think of. I didn't even bother filtering anything, all of the site is in there. Even the search page itself. You can see that file under &lt;a href="/search_index.json"&gt;search_index.json&lt;/a&gt;, and it just contains a text-only version of all the content on this site. Or any Heron site that uses the newest version of Heron and has search-index enabled, for that matter&lt;label for="mn-count" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-count" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;There must be millions out there!&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Using this functionality is as simple as passing &lt;code&gt;--generate-search&lt;/code&gt; when generating the site&lt;label for="mn-cache" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-cache" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Remember to remove the cache before doing that, otherwise it won't pick up the correct search contents. Bonus fun fact: it takes about 15 seconds to regenerate this site completely without the cache.&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Took about an hour to write and is something like 50 lines of python. It's on the &lt;a href="https://gitlab.com/jspielmann/heron/-/blob/main/src/heron/search.py"&gt;repository&lt;/a&gt;, in case you want to have a look.&lt;/p&gt;
&lt;p&gt;&lt;span class="marginnote"&gt;Ugh!&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a name="tutorial"&gt;Javascript&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The more involved part is actually the Javascript implementation here, even though the end result isn't super complicated either. The most extravagant part is the timer that waits a bit before it does the actual search (and that isn't even needed, really). &lt;/p&gt;
&lt;p&gt;I tried &lt;a href="https://www.fusejs.io/"&gt;Fuse.js&lt;/a&gt; but the search results were not good. Then I tried &lt;a href="https://github.com/leeoniya/uFuzzy"&gt;uFuzzy&lt;/a&gt; but that one can't search in objects. It does have a great comparison page, however, and that brought me to &lt;a href="https://github.com/lucaong/minisearch"&gt;MiniSearch&lt;/a&gt;, which is what I'm using now.&lt;/p&gt;
&lt;p&gt;Let's go through the sources quickly, just so I don't forget how I did this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cm"&gt;&amp;lt;!-- loading MiniSearch from its minified single-file-script --&amp;gt;&lt;/span&gt;
&lt;span class="cm"&gt;&amp;lt;!-- of course I vendored the file --&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/js/minisearch.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="cm"&gt;&amp;lt;!-- it&amp;#39;s just an input field --&amp;gt;&lt;/span&gt;
&lt;span class="cm"&gt;&amp;lt;!-- you may ignore these styles, I just added them here because my CSS does not include styling for inputs --&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
         &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;searchfield&amp;quot;&lt;/span&gt;
         &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text&amp;quot;&lt;/span&gt;
         &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Search...&amp;quot;&lt;/span&gt;
         &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;width:100%;padding:0.4em;font-size:21px;font-family:et-book,Palatino,&amp;quot;Palatino Linotype&amp;quot;,&amp;quot;Palatino LT STD&amp;quot;,&amp;quot;Book Antiqua&amp;quot;,Georgia,serif;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="cm"&gt;&amp;lt;!-- preparing some elements for display&lt;/span&gt;
&lt;span class="cm"&gt;     since I am not using any template libraries, I have to do this manually. It is not so bad,&lt;/span&gt;
&lt;span class="cm"&gt;     because the content is small enough to model with regular fields with `id`s --&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;results&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;display: none;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;result_header&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;result_list&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally, this &lt;em&gt;massive&lt;/em&gt; Javascript script:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// two convenience functions; not strictly necessary, but make the structure below a bit nicer&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;block&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;none&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// once we have finished searching, we need to show the results, this function isolates that&lt;/span&gt;
&lt;span class="c1"&gt;// could be inlined, but meh!&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;renderResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;searchPhrase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;`&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;result_header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`Results for “&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;searchPhrase&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;”`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;result_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// these are the search options; I arrived at those through rigorous scientific experiments!!!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;miniSearch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MiniSearch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;storeFields&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;searchOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;boost&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fuzzy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// this is where the actual magic happens: first we get the search index, extract its JSON and finally...&lt;/span&gt;
&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/search_index.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// once we have the data, we build the search index (this only happens once at the beginning)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;miniSearch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// this is called from the timer every time we do an actual search&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// note me cleverly putting the function in this closure!&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;miniSearch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchfield&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;renderResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;searchfield&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// here&amp;#39;s the only little bit of extravagance I made for myself: the timer&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// the idea is that on every input event, we re-start a timer; only when&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// the timer expires do we actually do the search. This makes the site&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// not lock up while the user is still typing.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Turns out, the actual search is fast enough, so I set the delay to 0; this&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// at least takes the search out of the event loop.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;searchfield&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oninput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;searchfield&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the readable version. For &lt;em&gt;production&lt;/em&gt; I minimised the script. By hand, of course.&lt;/p&gt;
&lt;p&gt;So, what did we learn today? A few things!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Modern JS has a very nice &lt;code&gt;for (const x of y)&lt;/code&gt; loop.&lt;/li&gt;
&lt;li&gt;Modern JS has proper string interpolation. When did that land?!&lt;/li&gt;
&lt;li&gt;Modern JS allows you to use elements directly from their &lt;code&gt;id&lt;/code&gt;. You &lt;em&gt;can&lt;/em&gt; still do &lt;code&gt;document.getElementById&lt;/code&gt; and this will probably be faster if you do a lot of accesses to the same document, but for me it was just not necessary.&lt;/li&gt;
&lt;li&gt;You can absolutely build interesting and useful things without JS frameworks and libraries. Just start building, and in less than a hundred lines of code you'll have created something nice!&lt;/li&gt;
&lt;/ul&gt;</description><pubDate>Sat, 11 Jan 2025 11:45:00 +0100</pubDate></item><item><title>Book of the Year (updated)</title><link>https://shezi.de//posts/2024-01-02-book-of-the-year-updated.html</link><description>&lt;p&gt;Every year, I find some book that's extraordinary, or different, or great, or just plain fun. This doesn't mean that the books came out in that year, just that I found them that year. I've not collected them formally up until now, so I'll have to catch up now.&lt;/p&gt;
&lt;p&gt;The following list is updated to include the years in descending order, with the newest entry on top.&lt;/p&gt;
&lt;h1&gt;2023&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Those Fascinating Numbers&lt;/strong&gt; by Jean-Marie De Koninck&lt;/p&gt;
&lt;p&gt;Not the &lt;em&gt;newest&lt;/em&gt; book on this list, and not quite easy to get, this one is an absolute &lt;em&gt;banger&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;From the fantastic chapter structure&lt;label for="mn-chapters" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-chapters" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;One per number, most often less than a page&lt;/span&gt; through the 20-page introduction and symbol definition, and the 209-entries-long references section, to the list of prime numbers less than 10000; this book has it all.&lt;/p&gt;
&lt;p&gt;Even though it skips a &lt;em&gt;few&lt;/em&gt; numbers before reaching its final destination at $10^10^10^34$, this is an absolute &lt;em&gt;must-read&lt;/em&gt;.&lt;/p&gt;
&lt;h1&gt;2021&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://www.buch7.de/produkt/immun-philipp-dettmer/1041333830?ean=9783864931758"&gt;Immun by Philipp Dettmer&lt;/a&gt; (&lt;a href="https://www.hugendubel.de/de/buch_gebunden/philipp_dettmer-immune-40425343-produkt-details.html"&gt;english version&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The graphics of "kurzgesagt" explain &lt;em&gt;in detail&lt;/em&gt; one of the most complicated machines we've found in nature: the human immune system. Not only is this very relevant today, it's also super-interesting and presented in an easy-to-digest manner and with humor.&lt;/p&gt;
&lt;h1&gt;2020&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://www.buch7.de/produkt/we-have-no-idea-jorge-cham/1030684253?ean=9781473660205"&gt;We have no idea by Jorge Cham and Daniel Whiteson&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So you think you know anything? Wrong! We know a small handful of things about "normal" matter, which just makes up &lt;em&gt;at most&lt;/em&gt; 5% of all matter/energy in the universe. The rest? We don't even know what the rest is. So, no we have no idea.&lt;/p&gt;
&lt;p&gt;The only bad thing about this book is that, after a while, you'll know that you have no idea. And the book will tell you again. And again... and again...&lt;/p&gt;
&lt;h1&gt;2019&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://www.buch7.de/produkt/how-to-invent-everything-ryan-north/1033884462?ean=9780753552568"&gt;How To Invent Everything by Ryan North&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Just imagine you're on a leisurely trip through time and your time machine breaks. What do you do now? Well, the first step is to find out which time period you're in. Hopefully, people are already around, because then step 2 will be much simpler: just re-build civilisation. For that, you'll have to invent everything. Fortunately, that's much simpler than you'd think, because humanity took &lt;em&gt;looooong&lt;/em&gt; breaks between the imporant steps. This book will give you the quick course.&lt;/p&gt;
&lt;h1&gt;2018&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://www.buch7.de/produkt/s-j-j-abrams/1021378326?ean=9780857864772"&gt;S. by J. J. Abrams and Doug Dorst&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a weird and wonderful book, because while the book contains a rather weird story, that's not the main thing. The main thing are the notes that are in the margin, where two people talk about the book, and find some great conspiracy around its author. The main event is not the story in the book, it's the story around the book, and the huge number of things that they put into the book. At one point, there's a paper towel with notes on it, between two pages. That kind of effort is just amazing, and the book is amazing.&lt;/p&gt;</description><pubDate>Thu, 01 Feb 2024 20:00:00 +0100</pubDate></item><item><title>Exploring all the concepts</title><link>https://shezi.de//posts/2023-06-02-alien-games.html</link><description>&lt;blockquote&gt;
&lt;p&gt;Every time I invent something new, some person on the internet has already made a classification of all the possible ways this could go.&lt;br /&gt;
  - Thomas Edison, probably&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We humans are astonishing collectors and explorers. Once we seize on a concept or an idea, we categorise until every last nook and cranny is catalogued.&lt;/p&gt;
&lt;p&gt;Here are &lt;a href="https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id=7041"&gt;10000 types of beetles&lt;/a&gt;.&lt;br /&gt;
Here are &lt;a href="https://de.wikipedia.org/wiki/Schmeil-Fitschen"&gt;1300 pages of plant species in their 96th edition&lt;/a&gt;, organised in such a ridiculous fashion it'll take you three hours to find the one you have in your hand&lt;label for="mn-schmeilfitschen" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-schmeilfitschen" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;I had to endure this with a former girlfriend, and for some reason she just wouldn't listen to me trying to organise her plant taxonomy differently.&lt;/span&gt;. &lt;a href="https://www.gutenberg.org/ebooks/58683"&gt;The book is so old you can read it on Gutenberg.org&lt;/a&gt;&lt;br /&gt;
Here are &lt;a href="https://en.wikipedia.org/wiki/Star_catalogue"&gt;11 different ways to list the stars in the sky&lt;/a&gt;, plus nine more specialised catalogues.&lt;/p&gt;
&lt;p&gt;But we also do this to the things we make up ourselves. Here are &lt;a href="https://www.automobile-catalog.com/"&gt;catalogues of cars&lt;/a&gt;, &lt;a href="https://www.catcar.info/"&gt;of car parts&lt;/a&gt; and of &lt;a href="https://autocatalogarchive.com/"&gt;car catalogues&lt;/a&gt;.&lt;br /&gt;
Here are databases for &lt;a href="https://www.cia.gov/the-world-factbook/"&gt;world facts&lt;/a&gt;, &lt;a href="https://www.nps.gov/subjects/nationalhistoriclandmarks/search.htm"&gt;landmarks&lt;/a&gt;, &lt;a href="https://fdc.nal.usda.gov/"&gt;foodstuffs&lt;/a&gt;, &lt;a href="https://www.jokecamp.com/blog/guide-to-football-and-soccer-data-and-apis/"&gt;football&lt;/a&gt;, &lt;a href="https://www.moma.co.uk/public-domain-images/"&gt;artwork&lt;/a&gt; and of course, &lt;a href="https://www.freecodecamp.org/news/https-medium-freecodecamp-org-best-free-open-data-sources-anyone-can-use-a65b514b0f2d/"&gt;databases&lt;/a&gt;, &lt;a href="https://github.com/awesomedata/awesome-public-datasets"&gt;databases&lt;/a&gt; and &lt;a href="https://docs.google.com/spreadsheets/d/1wZhPLMCHKJvwOkP4juclhjFgqIY8fQFMemwKL2c64vk/edit#gid=0"&gt;databases&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And also we catalogue art and fictional things, like &lt;a href="https://www.imdb.com/"&gt;movies&lt;/a&gt;, &lt;a href="https://musicbrainz.org/"&gt;songs&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/List_of_Middle-earth_characters"&gt;story characters&lt;/a&gt;.&lt;br /&gt;
And finally, we also do this to concepts, like in maths, physics, or &lt;a href="https://balioc.tumblr.com/post/628726469386960897/a-taxonomy-of-magic"&gt;magic&lt;/a&gt; and &lt;a href="https://boardgamegeek.com/browse/boardgamemechanic"&gt;board game mechanics&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Which is why it is exciting when someone tries to push the boundaries of some of these categories. I know I've tried with &lt;a href="https://habeamus.com/"&gt;our boardgame&lt;/a&gt;. And, sure, it is easy to create new &lt;em&gt;implementations&lt;/em&gt; of a story character, or car, or country, or board game mechanic. But to define a new category, a new mechanic itself, that is &lt;em&gt;very, very hard&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I was so pleased when I found three entries in a discussion on games played by aliens: &lt;a href="https://blog.plover.com/notes/alien-poker.html"&gt;Notes on card games played by aliens&lt;/a&gt;, &lt;a href="https://novalis.org/blog/2023-05-02-rarely-seen-game-mechanics.html"&gt;Rarely seen game mechanics&lt;/a&gt; and &lt;a href="https://blog.plover.com/notes/game-mechanics.html"&gt;Notes on rarely-seen game mechanics&lt;/a&gt;. And while these articles are somewhat entertaining, they don't really contain "rare" or new, or alien mechanics or ideas. Sure, chopping off a finger to make the opponent take a move back is audacious and well worth a short story, it's not really a new game mechanic, just a more extreme version of &lt;a href="https://boardgamegeek.com/boardgamemechanic/2847/advantage-token"&gt;Advantage Token&lt;/a&gt;, or possibly even really just a regular game economy, though spread out over the meta-game&lt;label for="mn-category" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-category" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;I am however tempted to mention &lt;a href="https://boardgamegeek.com/boardgamemechanic/2040/hand-management"&gt;Hand Management&lt;/a&gt; and &lt;a href="https://boardgamegeek.com/boardgamemechanic/2906/i-cut-you-choose"&gt;I Cut, You Choose&lt;/a&gt;&lt;/span&gt;. The other things they discuss are all just instances of well-known mechanics, all of which appear in a multitude of games. They are just "rare" if you're unfamiliar with board games in general.&lt;/p&gt;
&lt;p&gt;All of this is just a long-winded way of saying this: if you have invented, or want to invent, or are about to invent a &lt;em&gt;new game mechanic&lt;/em&gt;, please please please let me know!&lt;/p&gt;</description><pubDate>Fri, 02 Jun 2023 20:00:00 +0200</pubDate></item><item><title>“No New Tabs!” A Firefox extension</title><link>https://shezi.de//posts/2023-03-31-no-new-tabs.html</link><description>&lt;p&gt;There are some sites that will just open each link in a new tab. And I hate it.&lt;/p&gt;
&lt;p&gt;I hate that they content-optimized themselves to the point where squeezing out another tenth of a percent&lt;label for="mn-imagine" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-imagine" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;I imagine.&lt;/span&gt; of user retention is worth annoying &lt;em&gt;me&lt;/em&gt; with that nonsense.&lt;/p&gt;
&lt;p&gt;So I had to take a radical step: I searched for an extension that removes that attribute from all links everywhere. And I didn't find any. But, can you imagine, there are extensions that do the &lt;em&gt;opposite&lt;/em&gt;, open every link in a new tab.&lt;/p&gt;
&lt;p&gt;So I had to take a radical step: I modified one of these extensions to do what I wanted. Thank you, &lt;a href="https://github.com/ahgolden/new-tab"&gt;ahgolden, for writing the best extension template ever&lt;/a&gt;.&lt;br /&gt;
So here it is, &lt;a href="https://addons.mozilla.org/addon/nonewtab/"&gt;No New Tab!&lt;/a&gt;, and &lt;a href="https://github.com/shezi/no-new-tab"&gt;here's the source code&lt;/a&gt;, with all of its glorious four lines of code&lt;label for="mn-code" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-code" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Well, if you count a single &lt;code&gt;}&lt;/code&gt; as a separate line&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;And that's all.&lt;/p&gt;
&lt;h2&gt;One more thing&lt;/h2&gt;
&lt;p&gt;Oh wait, there is one more thing: I tried building this for myself and then just installing it. No need for anyone else to know about my superpowers. But Firefox won't let me install the extension, since it's not signed. Even if I disable the &lt;code&gt;xpinstall.signatures.required&lt;/code&gt; setting -- because that one only works in Firefox Developer Edition. I installed FFDEV and tried out the extension&lt;label for="mn-works" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-works" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;It works.&lt;/span&gt;, then submitted for signing. You can choose between “self-distributed” and “hosted”, but I didn't want the hassle so I let them host it for me. Lots of scary language around “we might check and ban your extension at any time!!!” Well, go ahead, review my four-line extension!&lt;br /&gt;
Naturally, I made a mistake during the upload and had to upload it again. With a new ID, but ok. It also &lt;em&gt;needs&lt;/em&gt; an ID, which I found out from some forum thread where someone had the same problem.&lt;/p&gt;
&lt;p&gt;So in a way, you can use my extension as the absolute minimum of what an extension needs.&lt;/p&gt;</description><pubDate>Fri, 31 Mar 2023 20:30:00 +0200</pubDate></item><item><title>Why static site generation?</title><link>https://shezi.de//posts/2023-01-10-static-site-generator.html</link><description>&lt;p&gt;I was recently asked &lt;a href="https://python-podcast.de/show/jahresruckblick-2022-und-ungelesene-macbooks/?t=1%3A54%3A37%2C2%3A02%3A13"&gt;Why a static site generator?&lt;/a&gt; Especially since &lt;a href="/posts/2021-06-03-djangocon.html"&gt;I can work fairly well with Django&lt;/a&gt;. So, fair question.&lt;/p&gt;
&lt;p&gt;When I start programming something that I want, I'm not in "professional programming" mode, but instead in exploratory mode. That, too, is something I talked about in the podcast. This means that I rarely plan ahead for very long, instead I try to reach concrete goals as simply as possible. You know, "agile".&lt;br /&gt;
This means that I don't care so much about doing it "right", but instead about doing it "fast". So here's the first answer to the question: When I was re-doing my blog&lt;label for="mn-again" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-again" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Again&lt;/span&gt;, this was the simplest way to do it. I had already had a static site generator that didn't do what I wanted&lt;label for="mn-acshully" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-acshully" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Or rather, that I couldn't get to do what I wanted. Failure here is my own, not that of the software.&lt;/span&gt; so I replaced it with a different SSG: &lt;em&gt;mine&lt;/em&gt;.&lt;br /&gt;
And that is perfectly fine for exploring and learning new programming stuff. I'm doing this for my entertainment, after all.&lt;/p&gt;
&lt;p&gt;But there is a more general answer to the question "why static site generation", and like all software development, there are trade-offs to be made. The short summary is this:
* minus: I'm not an expert
* minus: less library support during development
* minus: no dynamic user interface
* minus: no dynamic content
* plus: exact points in time for updates
* plus: simple hosting
* plus: more security (due to simple hosting)
* plus: fast responses (due to simple hosting)&lt;/p&gt;
&lt;p&gt;Let's take them one after the other.&lt;/p&gt;
&lt;h2&gt;I'm not an expert.&lt;/h2&gt;
&lt;p&gt;This is mainly a minus, apart from the exploratory/learning angle I mentioned before. But in general, one should try to do things that one is good at, because the expected result is better. With this, my first instict of course was to do something in Django. I &lt;em&gt;know&lt;/em&gt; Django, and I'm very good with it. So I know that I could to this, and I could do it well.&lt;/p&gt;
&lt;p&gt;But it's also kind of boring: another CMS, yay. So not exactly the greatest thing to do in my less-than-reams-of spare time.&lt;/p&gt;
&lt;p&gt;So in my situation, the "new thing"-angle was more important. But this is something that you need to be aware of when designing a software solution: are you doing this because you &lt;em&gt;know&lt;/em&gt; what to do, or because it's new and shiny and different, or because it's the best fit? In a professional setting, one should always go for the best fit.&lt;/p&gt;
&lt;h2&gt;Less library support during development&lt;/h2&gt;
&lt;p&gt;Let's face it: &lt;a href="https://pypi.org/project/Zope/"&gt;there&lt;/a&gt; &lt;a href="https://pypi.org/project/Flask/"&gt;are&lt;/a&gt; &lt;a href="https://pypi.org/project/CherryPy/"&gt;many&lt;/a&gt; &lt;a href="https://pypi.org/project/Django/"&gt;libraries&lt;/a&gt; &lt;a href="https://rubyonrails.org/"&gt;in&lt;/a&gt; &lt;a href="https://laravel.com/"&gt;many&lt;/a&gt; &lt;a href="https://spring.io/projects/spring-boot"&gt;languages&lt;/a&gt; that allow you to do dynamic web applications. It's an interesting space and some of the solutions are absolutely fantastic. So one thing you can do is just choose one, follow the tutorials or your expertise and create something excellent.&lt;/p&gt;
&lt;p&gt;Great!&lt;/p&gt;
&lt;p&gt;The library situation for static site generation is... uh... sparse. I don't know of any. Didn't search either, but I wouldn't even know what to search for. So, there's much less guidance when creating an SSG instead of a dynamic web site.&lt;/p&gt;
&lt;p&gt;And this is an important point: while it's often &lt;em&gt;great fun&lt;/em&gt; to do something different, it's not always advisable. There are good reasons for the prevalence of dynamic web sites, and you should choose the right technical architecture for the problem you're solving.&lt;/p&gt;
&lt;h2&gt;No dynamic user interface&lt;/h2&gt;
&lt;p&gt;This is kind-of obvious. A static site will not have a dynamic user interface. No admin interface. No forms. No built-in way to see and edit the content.&lt;/p&gt;
&lt;p&gt;If you need that, you need to find a different solution. Either by going fully dynamic, or maybe a sort-of hybrid solution with a dynamic editing interace and then a static site&lt;label for="mn-hybridsolutions" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-hybridsolutions" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Here are two options: &lt;a href="https://github.com/wagtail/wagtail-bakery"&gt;Wagtail Bakery&lt;/a&gt;, &lt;a href="https://www.getlektor.com/"&gt;Lektor&lt;/a&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;No dynamic content&lt;/h2&gt;
&lt;p&gt;This is &lt;em&gt;the most obvious difference&lt;/em&gt; between a static site and a dynamic site. One is fixed and doesn't change, the other is dynamic and can change on every request.&lt;/p&gt;
&lt;p&gt;In case you might have missed this: that's the point about &lt;em&gt;dynamic web sites&lt;/em&gt;. They are dynamic, d'uh!&lt;/p&gt;
&lt;p&gt;And if you need dynamic content, that's really about the only thing you can do. Have a database, store your data there, build the site "on the fly" when someone requests it. Fine!&lt;/p&gt;
&lt;p&gt;But do you really need it? Sometimes you might not need it.&lt;/p&gt;
&lt;h2&gt;Exact points in time for updates&lt;/h2&gt;
&lt;p&gt;You know that one of the hardest problems in computer science is cache invalidation. So with a dynamic site you go full in one direction: no cache at all, so nothing to invalidate. Easy and always fresh!&lt;/p&gt;
&lt;p&gt;But that's usually not enough. In one of my first commercial projects we had to interact with an API that would give us product prices. Since we had about 20 products per page, we'd have to call the API 20 times, and that was just too slow. So what did we do? Yes, we stored the prices in our own memory, in a memcached. Super-fast, but: how long are you supposed to keep these prices? At what point in time do you renew them? What about products going out of stock. We needed some cache invalidation and, honestly, that was the hardest thing about the entire project.&lt;/p&gt;
&lt;p&gt;This is a rather more general process: you have a dynamic site, it's too slow, you put some caching in front of it (the Django cache, a memcached, squid, etc), done&lt;label for="mn-cache" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-cache" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;See how many cache servers you can spot in the &lt;a href="https://meta.wikimedia.org/wiki/Wikimedia_servers#/media/File:Wikipedia_webrequest_flow_2020.png"&gt;Wikipedia server infrastructure&lt;/a&gt;!&lt;/span&gt;! &lt;em&gt;But&lt;/em&gt;, not really! Now you need to keep your caches fresh. You want your content to respond dynamically, that's the point of your site, so you must make sure that the caches aren't stale. And thus, cache invalidation.&lt;/p&gt;
&lt;p&gt;But what if you look at the problem from the other side. What if you start not from a &lt;em&gt;continually changing site&lt;/em&gt; and delete cached content that has changed, but instead start from a &lt;em&gt;constant, unchanging site&lt;/em&gt; and add singular points in time when the content can change. So whenever some content changes, you &lt;em&gt;push&lt;/em&gt; the change into the site, instead of pulling from a dynamic data set into your cache.&lt;/p&gt;
&lt;p&gt;And thus, static site generation. I have a set of data (in the form of markdown files, but could be anything), and whenever I am happy with the state that they're in&lt;label for="mn-mistakes" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-mistakes" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;I mean, except when I make a mistake and publish a draft.&lt;/span&gt;, I regenerate everything and push it live.&lt;/p&gt;
&lt;p&gt;There are now exactly-defined points in time when my site changes, and that fits my use case perfectly. &lt;/p&gt;
&lt;h2&gt;Simple hosting&lt;/h2&gt;
&lt;p&gt;Not having to regenerate the site whenever it is requested also makes serving the site much simpler. There's a directory of files, go serve that. &lt;em&gt;Any&lt;/em&gt; webserver can do that. No need for complex infrastructure, reverse proxying, app server, database, media and static serving; just simple, easy file hosting.&lt;/p&gt;
&lt;p&gt;Heck, it's so simple, you could &lt;a href="https://www.learnaws.org/2017/10/22/create-site-aws-using-s3/"&gt;put it on an S3 bucket&lt;/a&gt; and call it a day.&lt;/p&gt;
&lt;p&gt;Heck, it's so simple, &lt;a href="https://pages.github.com/"&gt;Github will do it for you for free&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;With simple hosting come other benefits. You don't need to worry about someone breaching your database because there is no database. Remove all nginx plugins and you'll have a digital fortress as secure as it can be.&lt;/p&gt;
&lt;p&gt;And it'll be fast, too. Simple file serving is fast on its own, but if it's not fast enough, put your site on a ramdisk and you'll saturate all the networks you have. My fully generated site is about 12MB large. I can afford that much ramdisk.&lt;/p&gt;
&lt;h1&gt;in Summary&lt;/h1&gt;
&lt;p&gt;Look, I just &lt;em&gt;wanted&lt;/em&gt; to do this, ok? It works for me, and I'm having fun.&lt;/p&gt;
&lt;p&gt;Are there more reasons needed?&lt;/p&gt;</description><pubDate>Tue, 10 Jan 2023 20:30:00 +0100</pubDate></item><item><title>Art</title><link>https://shezi.de//posts/2022-12-10-Art.html</link><description>&lt;p&gt;(note: this is a work in progress, I'm still thinking about it)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“The anthropologists got it wrong when they named our species Homo sapiens ('wise man'). In any case it's an arrogant and bigheaded thing to say, wisdom being one of our least evident features. In reality, we are Pan narrans, the storytelling chimpanzee.”&lt;br /&gt;
― Terry Pratchett, "The Globe"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I was wondering why we &lt;em&gt;do&lt;/em&gt; things. Like, sure, we need food and shelter to survive, and probably a few other basic necessities. And if those aren't met, at least to some degree, few other things are important.&lt;/p&gt;
&lt;p&gt;But that's not what we strive for most of the time, is it? What we strive for isn't &lt;em&gt;just&lt;/em&gt; food and shelter, it isn't &lt;em&gt;just&lt;/em&gt; survival, it isn't &lt;em&gt;just&lt;/em&gt; belonging-to-a-group-because-that-increases-chances-of-survival.&lt;br /&gt;
Why do people read books? Why do we listen to music? Why do people go to galleries and look at paintings? Why do we sit around camp fires and tell stories? Why do people risk their lives to go to the theater and the opera and meet people and sing and dance?&lt;/p&gt;
&lt;p&gt;I want to go a step further than &lt;em&gt;pan narrans&lt;/em&gt;, because I believe stories aren't the ultimate goal. The ultimate goal are &lt;em&gt;feelings&lt;/em&gt;. Emotions.&lt;br /&gt;
We are the feeling ape, &lt;em&gt;simia sensibilis&lt;/em&gt;.&lt;br /&gt;
We need emotions so much that we'll spend considerable resources to experience them. We crave emotions so much that we'll destroy our lives to get them, to feel them. We'll hurt other people, and ourselves, to safeguard "the right emotions", and we'll expend all our energy to turn "bad feelings" into "good feelings", even to the detriment of all the world.&lt;/p&gt;
&lt;p&gt;Stories are just one way that we make others feel things, even though a very strong and durable way. We tell stories so others can experience feelings "second-hand". We &lt;em&gt;invent&lt;/em&gt; stories so we can show other people emotions that they wouldn't think of themselves. We read books because someone told us that they liked to feel the emotions wrapped into the words.&lt;br /&gt;
But there are so many other ways to achieve feeling transfer: painting, music, dance, food, rituals, social groups, personal relationships. Do you think it's a coincidence that &lt;em&gt;all&lt;/em&gt; human cultures have these? And cherish them? And develop them and refine them, and defend them against outsiders and change. It is because we crave &lt;em&gt;feelings&lt;/em&gt;.&lt;br /&gt;
Our rational thinking abilities, that we are so proud of, are just a mechanism to get at more feelings, and more variation in feelings.&lt;/p&gt;
&lt;p&gt;And thus, Art.&lt;br /&gt;
Art is making other people feel feelings. Stories are Art, paintings are Art, music is Art, dance is Art, food is Art. Anything that one does to make other people feel something is Art. And it doesn't matter how big or small, or for how many or few people the Art is for.&lt;br /&gt;
If you make a movie and it reaches a billion people: Art.&lt;br /&gt;
If you prepare a sandwich for your friend: Art.&lt;br /&gt;
If you write the best symphony in the world and are on all the news: Art.&lt;br /&gt;
If you dance with someone: Art.&lt;br /&gt;
If you sing a tune to yourself, just for yourself: Art.&lt;br /&gt;
If you create the worlds best-selling computer game: Art.&lt;br /&gt;
If you create a computer game but don't show anyone: Art.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Every one of us is an Artist.&lt;/em&gt; It is what we are.&lt;/p&gt;
&lt;p&gt;Do not let anyone tell you that it's not Art you are creating. Do not let anyone tell you that your Art is bad, or that someone else is better, or that you shouldn't do Art. As long as anyone is feeling something over the things you do, even if it's just yourself, it is Art as much as anything else.&lt;br /&gt;
You do your Art and you be proud of it.&lt;/p&gt;
&lt;p&gt;But there is also another side to it. We do not simply crave &lt;em&gt;good emotion&lt;/em&gt;, we crave &lt;em&gt;any emotion&lt;/em&gt;, and the more variety there is, the better. That also means that we need &lt;em&gt;fear&lt;/em&gt; and &lt;em&gt;tension&lt;/em&gt; and &lt;em&gt;sadness&lt;/em&gt;. How boring would stories be if they were only ever happy.&lt;/p&gt;
&lt;p&gt;And also note that Art has nothing to do with money. You can make money without doing Art and you can do Art without making money.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Every one of us is an Artist.&lt;/em&gt; It is what we are. You do your Art and you be proud of it.&lt;/p&gt;</description><pubDate>Sat, 10 Dec 2022 15:31:00 +0100</pubDate></item><item><title>All the best paths</title><link>https://shezi.de//posts/2022-10-03-mapstuff.html</link><description>&lt;style&gt;
.codehilite { border: 1px solid black; }
&lt;/style&gt;

&lt;p&gt;When you go from A to B on a map, there is usually a best path to go&lt;label for="mn-nav" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-nav" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;I mean, that's what satnav is for, right?&lt;/span&gt;. But since there are many roads, there must be points where there are two paths that are optimal.&lt;/p&gt;
&lt;p&gt;I have this silly idea that I want to find these watershed points and draw a funny little map showing the hills between them. In this post, we'll start looking at OSM data and play around a bit to find the watersheds.&lt;br /&gt;
This isn't a finished thing. I played around until I didn't want to any more. What follows is part of a Jupyter notebook in which I tried getting somewhere&lt;label for="mn-joke" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-joke" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;&lt;em&gt;Getting somewhere!&lt;/em&gt; Do you get it?&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The first thing we need to do is get some OSM data. I used the excellent &lt;a href="https://extract.bbbike.org/"&gt;BBike OSM extractor&lt;/a&gt; and found me some Düsseldorf.&lt;/p&gt;
&lt;p&gt;These are the libraries we're going to use:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;collections&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;tracemalloc&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;svgwrite&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pyproj&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, we're going to read the map data, in its entirety. Note that this may take a little while. Depending on the data file, this might be between 2 and ∞ seconds. &lt;/p&gt;
&lt;p&gt;Just out of interest, we're also going to &lt;a href="https://docs.python.org/3/library/tracemalloc.html"&gt;tracemalloc&lt;/a&gt; it, just to see how much memory we're using.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;planet_6.74867,51.21731_6.79149,51.23367.osm.geojson&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;6.74867&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;51.21731&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;6.79149&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;51.23367&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;%%&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="n"&gt;tracemalloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;mapfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mapdata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapfile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tracemalloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_traced_memory&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;tracemalloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(69823463, 92864418)
CPU times: user 1.7 s, sys: 98.2 ms, total: 1.8 s
Wall time: 1.79 s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;700MB of allocations from an 11MB geojson file. Nice!&lt;/p&gt;
&lt;p&gt;I flailed around a little bit, trying to find out how to work with the data. I'm only showing a little bit here, just so you can see how &lt;em&gt;professional&lt;/em&gt; I am.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;features&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;29824&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Counter({&amp;#39;Point&amp;#39;: 10186, &amp;#39;LineString&amp;#39;: 11594, &amp;#39;MultiPolygon&amp;#39;: 8044})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Point&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;10186&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;line_strings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;LineString&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line_strings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;11594&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;multi_polygons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;MultiPolygon&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multi_polygons&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;8044&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alright, we need to go a bit deeper into the properties:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line_strings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

&lt;span class="n"&gt;types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;highways&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;junctions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line_strings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
    &lt;span class="n"&gt;highways&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;highway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
    &lt;span class="n"&gt;junctions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;junction&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;highways&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;junctions&lt;/span&gt;
&lt;span class="c1"&gt;# turns out, only highways are interesting&lt;/span&gt;
&lt;span class="n"&gt;highways&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Counter({&amp;#39;trunk&amp;#39;: 32,
         &amp;#39;secondary&amp;#39;: 287,
         None: 8421,
         &amp;#39;footway&amp;#39;: 1039,
         &amp;#39;residential&amp;#39;: 309,
         &amp;#39;tertiary&amp;#39;: 124,
         &amp;#39;track&amp;#39;: 19,
         &amp;#39;path&amp;#39;: 370,
         &amp;#39;unclassified&amp;#39;: 4,
         &amp;#39;living_street&amp;#39;: 10,
         &amp;#39;cycleway&amp;#39;: 14,
         &amp;#39;steps&amp;#39;: 233,
         &amp;#39;service&amp;#39;: 511,
         &amp;#39;pedestrian&amp;#39;: 125,
         &amp;#39;primary&amp;#39;: 11,
         &amp;#39;secondary_link&amp;#39;: 4,
         &amp;#39;platform&amp;#39;: 19,
         &amp;#39;trunk_link&amp;#39;: 48,
         &amp;#39;elevator&amp;#39;: 6,
         &amp;#39;tertiary_link&amp;#39;: 3,
         &amp;#39;corridor&amp;#39;: 5})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We found out that only highways are interesting. You know, &lt;a href="https://wiki.openstreetmap.org/wiki/Key:highway"&gt;we could have looked at the  documentation, too!&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These are the roads we are going to work with from now on:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;highways&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line_strings&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;highway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;motorway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;motorway_link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;primary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;secondary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;trunk&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;tertiary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;unclassified&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;residential&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;living_street&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pedestrian&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;primary_link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;secondary_link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;tertiary_link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;bridleway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;platform&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;highways&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;9349&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since we want to produce beautiful maps (or any maps, actually), we're going to need to be able to draw them. We do this by creating SVG files. These can be zoomed and printed and edited, as well as shown inline.&lt;/p&gt;
&lt;p&gt;Here's the utility function we're going to use. Note again &lt;em&gt;how professional this is&lt;/em&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;draw_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;roads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;segments&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;segments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HEIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;140&lt;/span&gt;

    &lt;span class="n"&gt;dwg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;svgwrite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Drawing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;svgwrite-example.svg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;WIDTH&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;mm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;HEIGHT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;mm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;transg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dwg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bounds_points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bounds_points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;roads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bounds_lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;roads&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
                &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;roads&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
            &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;roads&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
                &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;roads&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bounds_lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bounds_points&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;bounds_lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
            &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bounds_points&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;bounds_lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bounds_points&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;bounds_lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
            &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bounds_points&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;bounds_lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;max_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;PT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;WIDTH&lt;/span&gt;

    &lt;span class="c1"&gt;# g.translate(-(bounds[0][0] + scale/2), -bounds[0][1] - scale/4)&lt;/span&gt;
    &lt;span class="c1"&gt;# scale /= 2&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;transg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mf"&gt;1.4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# transg.scale(0.01, 0.01)&lt;/span&gt;
    &lt;span class="n"&gt;innerg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;innerg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# g = innerg&lt;/span&gt;

    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;none&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;svgwrite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;linecap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;round&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;linejoin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;round&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;white&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;strokes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;motorway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;svgwrite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;width&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PT&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;raceway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;svgwrite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;width&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PT&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;primary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;svgwrite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;width&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PT&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;secondary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;svgwrite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;width&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PT&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;tertiary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;svgwrite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;width&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PT&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;highway&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;roads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# print(highway[&amp;quot;geometry&amp;quot;][&amp;quot;coordinates&amp;quot;])&lt;/span&gt;
        &lt;span class="c1"&gt;# print(strokes.get(highway[&amp;quot;properties&amp;quot;][&amp;quot;highway&amp;quot;], {}))&lt;/span&gt;
        &lt;span class="n"&gt;coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;highway&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;max_y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;polyline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;stroke&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strokes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;highway&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;highway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;max_y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;max_y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;CIRC_R&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PT&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;max_y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;svgwrite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CIRC_R&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;none&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;max_y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CIRC_R&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#00ffff&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;none&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dwg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's draw a map of our data for the first time:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;%%&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="c1"&gt;#    ((9.08915, 49.04266), (0, 100, 1))&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;highways&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line_strings&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;highway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;motorway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;motorway_link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;primary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;secondary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;trunk&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;tertiary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;unclassified&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;residential&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;living_street&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pedestrian&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;primary_link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;secondary_link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;tertiary_link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;bridleway&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;platform&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="c1"&gt;# bounds = (8.9178,48.9743), (9.2605,49.1111)&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;105&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# points.append(p)&lt;/span&gt;
&lt;span class="n"&gt;draw_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;highways&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;CPU times: user 65 ms, sys: 191 µs, total: 65.2 ms
Wall time: 64.1 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;figure class="fullwidth"&gt;
  &lt;img src="/_images/maps/1.svg" alt="svg crude line rendering of Düsseldorf inner city map data"&gt;
  &lt;small&gt;Well that actually vaguely looks like a map of Düsseldorf.&lt;/small&gt;
&lt;/figure&gt;

&lt;h1&gt;1: move to meter-based coordinates&lt;/h1&gt;
&lt;p&gt;At some point, we're going to want to measure distances. To do so, we need to convert from WGS84/EPSG:4326 to a more well-suited coordinate system. Apparently &lt;a href="https://epsg.io/3035"&gt;EPSG 3035&lt;/a&gt; is the way to go here, since it's accurate for all of Europe.&lt;/p&gt;
&lt;p&gt;Or, hmm, we could also try these ones: &lt;a href="https://epsg.io/3068"&gt;EPSG:3068&lt;/a&gt;, &lt;a href="https://epsg.io/5679-15949"&gt;EPSG:5679&lt;/a&gt;, or &lt;a href="https://epsg.io/7837"&gt;EPSG:7837&lt;/a&gt; or any of 100 others. I don't know what the differences are and I guess they are minimal. I hope they are, anyway.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;pts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pyproj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;epsg:4326&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;epsg:3035&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;([6.7606583, 51.2234957], (0, 0, 0))
(-513271.50645004585, 9054181.35310964)
([6.7653554, 51.2195793], (0, 0, 0))
(-513019.06060262443, 9053634.032830823)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;9.08915&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;49.04266&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pyproj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Proj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;

&lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Proj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;epsg:3035&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preserve_units&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;p1x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;p2x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;distance_m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;p1x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p2x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p2x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;distance_m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;111156.71848944735&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pyproj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Transformer&lt;/span&gt;

&lt;span class="n"&gt;transformer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Transformer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_crs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;epsg:4326&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;epsg:3035&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;transformer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(-409100.56561648333, 8755738.426043686)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I have no idea what these numbers mean. But ok!&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;hw&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;highways&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;transformer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;602.7340984907527&lt;/span&gt;
&lt;span class="mf"&gt;160.7871203419805&lt;/span&gt;
&lt;span class="mf"&gt;91.58520679722832&lt;/span&gt;
&lt;span class="mf"&gt;34.37434665463454&lt;/span&gt;
&lt;span class="mf"&gt;11.082176061452335&lt;/span&gt;
&lt;span class="mf"&gt;115.85408874142807&lt;/span&gt;
&lt;span class="mf"&gt;150.24235157447842&lt;/span&gt;
&lt;span class="mf"&gt;65.0171774765028&lt;/span&gt;
&lt;span class="mf"&gt;456.24031388355354&lt;/span&gt;
&lt;span class="mf"&gt;110.21961116674738&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Those distances look... reasonable?&lt;/p&gt;
&lt;p&gt;Here we go, transforming our coordinates&lt;/p&gt;
&lt;p&gt;Careful: only run this once, or you'll kill all your coordinates! Why? Because I don't want to spend another 750MB of RAM on geo data again!&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# careful: only run this cell once!&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;hw&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;highways&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;hw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;transformer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transformer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;%%&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;hw&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;highways&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;coord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="c1"&gt;# points.add((home, (0, 100, 1)))&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;draw_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;highways&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;3458&lt;/span&gt;
&lt;span class="n"&gt;CPU&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;59.3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.83&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;to&lt;/span&gt;&lt;span class="n"&gt;tal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;63.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
&lt;span class="n"&gt;Wall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;61.4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;figure class="fullwidth"&gt;
  &lt;img src="/_images/maps/2.svg" alt="A slightly turned crude svg line rendering of Düsseldorf inner city map data"&gt;
  &lt;small&gt;That still looks a bit like Düsseldorf, though less so than before.&lt;/small&gt;
&lt;/figure&gt;

&lt;p&gt;You might notice that the map isn't pointing the same way as before. Yes, well, that's unfortunate, and apparently because these projections use different norths. We won't let that discourage us...&lt;/p&gt;
&lt;h1&gt;3: the Dijkstra algorithm&lt;/h1&gt;
&lt;p&gt;Before we can start actually computing distances, we need to find a suitable data structure for our graph.&lt;/p&gt;
&lt;p&gt;I guess, the simplest one is this: a dictionary, where the keys are nodes and the entries are lists of &lt;code&gt;(other, distance)&lt;/code&gt;. We'll have to take care that our graph is bi-directional, since we want to make sure that we can go everywhere.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    

&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;hw&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;highways&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coordinates&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;3458&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ok, so dijkstra, in short: You start at a node, and you mark it as DONE. Then you visit all the nodes you can reach and mark them as active, with their current distance.
Then you start visiting nodes: From the list of active nodes you select the one with the smallest distance. Mark that as DONE. Add all nodes you can reach (that are not DONE) as active with current_distance + distance. Repeat until all nodes are visited.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# meh, we&amp;#39;ll have to find the closest point to our home so we can start somewhere!&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;home_point&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;
&lt;span class="n"&gt;min_dist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;

&lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;home&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;((-512198.0851256219, 9054105.365061712),
 16.589845930492412,
 (-512208.83020984754, 9054118.004925357))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;[((-512192.63521925174, 9054100.065596774), 7.601697709388693), ((-512236.8403923488, 9054071.247561801), 51.63307563186496), ((-512185.5072865789, 9054116.24124847), 16.628092897033614)]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find_minimum_active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Find the minimum active node, remove it from the active set and return it.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_000&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt;
    &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt;

&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;curr_p&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;find_minimum_active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# print(&amp;quot;---&amp;quot;)&lt;/span&gt;
    &lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curr_p&lt;/span&gt;
    &lt;span class="c1"&gt;# print(curr)&lt;/span&gt;
    &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;

    &lt;span class="c1"&gt;# print(&amp;quot;-----&amp;quot;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;next_p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;next_d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_d&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
        &lt;span class="c1"&gt;# print(next_p, next_d)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;next_p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next_p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;next_d&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_d&lt;/span&gt;

&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# [(p, (100, 0, 0)) for p in done]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;3202&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ooh, not all points on the map are reachable from our starting point. There's actually quite a lot of roads we can't reach, mainly the southern Rheinbrücke, since the highway on-ramps are not part of my map section.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;%%&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="n"&gt;svg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;draw_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;highways&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;map.svg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;svg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;CPU times: user 1.11 s, sys: 10.6 ms, total: 1.12 s
Wall time: 1.13 s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;figure class="fullwidth"&gt;
  &lt;img src="/_images/maps/3.svg" alt="A slightly turned crude svg line rendering of Düsseldorf inner city map data with reachable points marked on it."&gt;
  &lt;small&gt;All reachable points in red.&lt;/small&gt;
&lt;/figure&gt;

&lt;p&gt;All right! We've visited all the reachable points. Now to color them!&lt;/p&gt;
&lt;h1&gt;4: determine colors based on max_distance/colormap&lt;/h1&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;colorcet&lt;/span&gt;

&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;colorcet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;256&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We're going to use &lt;a href="https://colorcet.com/"&gt;ColorCET&lt;/a&gt;, which is a better color map than traditional flame colors.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;max_dist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;slot_dist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;max_dist&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;slot_dist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;swatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colorcet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colorwheel&lt;/span&gt;
&lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;swatch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;svg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;draw_map&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;map.svg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;svg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;figure class="fullwidth"&gt;
  &lt;img src="/_images/maps/4.svg" alt="Endpoints of all reachable paths from the previous map, color-coded to show distance"&gt;
  &lt;small&gt;Rainbow-colored reachable points. Almost there!&lt;/small&gt;
&lt;/figure&gt;

&lt;p&gt;That's beautiful. But we have not found our watersheds yet. For that, we'll need to actually draw all the paths. Any segment that is &lt;em&gt;not&lt;/em&gt; part of a path is then a watershed. And so we need to adapt our dijkstra a little bit.&lt;/p&gt;
&lt;h1&gt;5: dijkstra with full paths&lt;/h1&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;segments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;sheds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find_minimum_active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Find the minimum active node, remove it from the active set and return it.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_p&lt;/span&gt;
    &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_p&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;middle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;


&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;curr_p&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;find_minimum_active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# print(&amp;quot;---&amp;quot;)&lt;/span&gt;
    &lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curr_p&lt;/span&gt;
    &lt;span class="c1"&gt;# print(curr)&lt;/span&gt;
    &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
    &lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;prev_p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# print(&amp;quot;-----&amp;quot;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;next_p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;next_d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_d&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
        &lt;span class="c1"&gt;# print(next_p, next_d)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;next_p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;next_p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_p&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;next_d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sheds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_p&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next_d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sheds&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(3202, 154, 3201)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;%%&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="n"&gt;swatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colorcet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rainbow&lt;/span&gt;
&lt;span class="c1"&gt;# points = [(p, swatch[255 - slot(dist)]) for p, dist in done.items()]&lt;/span&gt;
&lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;#ff0000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sheds&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;#00ffff&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="n"&gt;segs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;swatch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;svg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;draw_map&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;segs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;home_point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;map.svg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;svg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(-512198.0851256219, 9054105.365061712)
CPU times: user 549 ms, sys: 390 µs, total: 549 ms
Wall time: 544 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;figure class="fullwidth"&gt;
  &lt;img src="/_images/maps/5.svg" alt="A slightly turned crude svg line rendering of Düsseldorf inner city map data with reachable paths color-coded to show distance."&gt;
  &lt;small&gt;Ah, watershed points!&lt;/small&gt;
&lt;/figure&gt;

&lt;p&gt;And there we have it. All paths you can take in Düsseldorf, with watersheds marked in red. There are many things missing, but I stopped when I reached a nice picture.&lt;/p&gt;
&lt;p&gt;I must have made a mistake somewhere because there &lt;em&gt;should&lt;/em&gt; not be any closed circles. And also North should be up. And the map should be connected. And my paths should be little arrows to show flow direction. And directional roads should be directional. And footpaths should be different from highways. And the watersheds should be connected and probably hill-shaded, too. And I should show distance markers. And all of this should be much faster. And I'd like this in large-scale printable format. With rivers, and buildings, probably. You know, like in a real map.&lt;/p&gt;
&lt;p&gt;So, lots of stuff to do &lt;em&gt;next time&lt;/em&gt;.&lt;/p&gt;</description><pubDate>Mon, 03 Oct 2022 20:00:00 +0200</pubDate></item><item><title>Getting all mtimes in a directory recursively with Python</title><link>https://shezi.de//posts/2022-09-11-mtimes.html</link><description>&lt;p&gt;I need to get the mtimes for all files in a directory &lt;em&gt;quickly&lt;/em&gt;, so I can add a good caching layer to &lt;a href="https://gitlab.com/jspielmann/heron/#heron"&gt;Heron&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There's a lot of bad/beginner advice on the internet on how to do this, so I had to try it out myself. Here are three four ways to do it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;test_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;method1&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dirpath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirnames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filenames&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;filenames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
            &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getmtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirpath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;  

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;method2_1&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dirpath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirnames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filenames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dir_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fwalk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;filenames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
            &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dir_fd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dir_fd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_mtime&lt;/span&gt;  
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;  

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;method2_2&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dirpath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirnames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filenames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dir_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fwalk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;filenames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
            &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getmtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirpath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;  

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;method3&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  
    &lt;span class="n"&gt;file_paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;**/*&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;test_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;file_paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getmtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_dir&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;  

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;  1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;method1&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2_1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;method2_1&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2_2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;method2_2&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;  3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;method3&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here's the output for my specific test directory on a Linux computer:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;  1 2.029968542000006
2_1 1.4166910230001122
2_2 1.9437335370002984
  3 3.277476520000164
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that these are for 1000 runs each, so any of these methods will be quick enough in my case.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;glob&lt;/code&gt;-kind of method can be &lt;em&gt;much&lt;/em&gt; faster when you can discard many files, ie. if you only need all image files or some such. I need all files here, so this does not apply to me.&lt;/p&gt;
&lt;p&gt;I guess I &lt;em&gt;should&lt;/em&gt; implement a version using &lt;a href="https://docs.python.org/3/library/os.html?os.scandir"&gt;&lt;code&gt;scandir&lt;/code&gt;&lt;/a&gt; as well, but I don't expect a lot of speedup over method &lt;code&gt;2_1&lt;/code&gt;, since on Linux, each &lt;code&gt;stat&lt;/code&gt; needs a &lt;code&gt;stat&lt;/code&gt; anyway. So I'll go with &lt;code&gt;2_1&lt;/code&gt; for the moment.&lt;/p&gt;</description><pubDate>Sun, 11 Sep 2022 21:00:00 +0200</pubDate></item><item><title>Headphones</title><link>https://shezi.de//posts/2022-09-02-headphones.html</link><description>&lt;p&gt;I'm very particular about headphones. Not the sound, the sound is almost always fine anyway, but the &lt;em&gt;fit&lt;/em&gt; of the phones. In the office, I have fancy over-ears that are large enough to cover my ears and soft enough so I don't have to worry about wearing them all day.&lt;br /&gt;
But for on-the-go, I prefer earbuds, mainly for their better isolation, both inwards and outwards. But when I'm moving, or speaking, or eating, most earbuds will fall out, or at least lose their fit so the outside world intrudes. &lt;a href="https://discussions.apple.com/thread/250868943"&gt;I don't seem to be the only one with that problem.&lt;/a&gt;. Naturally, I want cables as well, and I prefer to have a microphone for calls.&lt;/p&gt;
&lt;p&gt;Many years ago, I used &lt;a href="https://www.beyerdynamic.de/unternehmen/presse/individualitaet-fuer-sportbegeisterte-beyerdynamic-erweitert-sein-angebot-an-in-e"&gt;Beyerdynamics fitted earbuds&lt;/a&gt;, which were perfect. They're molded to your ears by a specialist. But they're also quite expensive, and after breaking the headphones once and then losing one of the fitted pieces, I wanted a slightly less expensive option&lt;label for="mn-price" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-price" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Of course, if money is no object, &lt;a href="https://pro.ultimateears.com/collections/pro"&gt;there are much higher-end options&lt;/a&gt;.&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Some years later I found a model of &lt;a href="https://www.amazon.de/dp/B00T7WX6BC"&gt;Soul Pulse Earbuds&lt;/a&gt;, and those were perfect, too. Waterproof, great fit, microphone, inverted-design so no chance of losing them, and not very expensive either. Naturally, when I wanted to replace my old ones, I found out that this model was discontinued. So after buying the last specimens I could find, I had a problem: what to do once those break.&lt;/p&gt;
&lt;p&gt;Well, they're starting to break&lt;label for="mn-lost" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-lost" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;or get lost&lt;/span&gt;, so I need to find good replacements. As any normal person would do, I ordered several different models that look good and am now testing them all&lt;label for="mn-sound" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-sound" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;You'll notice that I won't compare on sound quality. This is because sound quality does not matter to me. They all sound equally good, or equally good enough, at least.&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Amazon Basics Sport In-Ear Headphones&lt;/h2&gt;
&lt;p&gt;21.72 €&lt;/p&gt;
&lt;p&gt;They are surprisingly not very cheap, compared to the other options in this lineup. However, they also bring with them everything that's necessary: earbuds, different ear fittings, ear wrap hoop and a carrying pouch. They come in an attractive brown cardboard box and there's an excellent manual that essentially tells you to put them into your ear.&lt;/p&gt;
&lt;p&gt;Fit is good, not great, but I like them. They are very round, so I think I could wear them for a long time. Having worn them for a while, I noticed that they are actually &lt;em&gt;too&lt;/em&gt; round, so they slip out of my ears all the time. Not exactly what I was looking for, and much worse than the JLab ones, which are rubbery and well-fitting.&lt;/p&gt;
&lt;p&gt;There has come up a sound issue, actually. There's a slight rattle in one of the headphones. I'll have to wait a while until I'm sure it's recurring.&lt;/p&gt;
&lt;h2&gt;Avantree TR504 Wired Earbuds For Sports&lt;/h2&gt;
&lt;p&gt;16.99 €&lt;/p&gt;
&lt;p&gt;I am very positively surprised by these. The cheap-looking site turned out a great product with excellent fit. They're quite bare, however: there is literally nothing in the box apart from the headphones. If you need different rubber earpiece-fitting-thingies, you better have some left from other headphones&lt;label for="mn-pieces" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-pieces" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;As you'd expect, I have a collection of about 100 of them.&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Right now, I have also no idea where I put them... soo... good start?&lt;/p&gt;
&lt;p&gt;They're also very round and comfortable to wear for a long time. And, so far, they managed to survive in my jacket pocket for some time, which is an important benchmark.&lt;/p&gt;
&lt;p&gt;Finally, they are also blue.&lt;/p&gt;
&lt;p&gt;I must say, as I'm wearing them more, I like them less. The very round form makes them slip out of my ears easily. So, not recommended.&lt;/p&gt;
&lt;h2&gt;JLAB JLab Fit&lt;/h2&gt;
&lt;p&gt;19.99 €&lt;/p&gt;
&lt;p&gt;These ones are &lt;em&gt;very&lt;/em&gt; branded, even on the rubber ear tips themselves. I'd thought that the rather sharp edges of the logo would be a problem, but so far, it's not. The added-logo-studdedness is balanced by their full rubber enclosure which I like very much. Unfortunately, they feel a tiny little bit too large for me, so I'm not sure I'll be able to wear them for a long time. There is a second set of rubber, uh, wrappings, maybe these will help. Funnily enough, the rubber covers don't fit the headphones very well, but they do fit my ears very well. They stick in and don't fall out, so that's nice. The rest of the assembly is a bit flimsy, but that's ok.&lt;/p&gt;
&lt;h2&gt;MEE audio, EP-M6PROG2-BK M6 PRO&lt;/h2&gt;
&lt;p&gt;49.99 €&lt;/p&gt;
&lt;p&gt;The most expensive headphones in this lineup mark themselves as "in-ear monitors". The sound is brilliant, just like for each of the other ones. They have great features, like the pouch they come with, or the replaceable cables&lt;label for="mn-cable" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-cable" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;not that I'd know where to get such a specific cable without the headphones&lt;/span&gt;, or the second non-microphoned cable, or the included 6.3mm adapter.&lt;br /&gt;
The real gimmick however are the included &lt;a href="https://www.complyfoam.com/"&gt;ComplyFoam&lt;/a&gt; tips, which sounds like an excellent (and expensive) idea. And I'd say it is. The tips stay in my ears, they shield sound excellently and they don't become very uncomfortable during longer use. So recommendation on this one.&lt;/p&gt;
&lt;p&gt;However, the actual headphone fit is not as good. The earpieces have rather sharp edges that become unfomfortable over time, so I'll have to give them a bit of a sanding. Also, they tend do slip out. I'm not sure I like them well enough for their price.&lt;/p&gt;
&lt;h2&gt;MEE Audio M6P2-WT G2&lt;/h2&gt;
&lt;p&gt;14.95 €&lt;/p&gt;
&lt;p&gt;They are white. They fit ok. They sound ok.&lt;/p&gt;
&lt;p&gt;I think I don't know where I put them, already.&lt;/p&gt;
&lt;h2&gt;Vivanco Sport Outdoor&lt;/h2&gt;
&lt;p&gt;14.00 €&lt;/p&gt;
&lt;p&gt;I didn't even order this one right away, because there are so many other good ones. And the reduced price is &lt;em&gt;not&lt;/em&gt; a good sign, given that I don't want to buy up the last few items again.&lt;br /&gt;
But now I've tried them and they are really good. Rubber coating all around, strong hang wire at the back, great fit for my ears. They even have volume buttons on the cable. They &lt;em&gt;look&lt;/em&gt; quite silly, because they're much heavier than the other ones, but I don't really care about that.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;In summary: I am surprised and astonished by the wide selection of great products, all of which fulfil my requirements. None have fatal flaws, except maybe the edgy &lt;em&gt;pro&lt;/em&gt; ones&lt;label for="mn-expense" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-expense" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;I am referring to the price, naturally.&lt;/span&gt;. Most of them are fine, some are better, some less. The most important thing about them is still the fit, so they won't fall out of my ears, and there are some great options there.&lt;/p&gt;
&lt;p&gt;For the foreseeable future, I'll have no problems with headphones, and that makes me happy.&lt;/p&gt;</description><pubDate>Fri, 02 Sep 2022 20:00:00 +0200</pubDate></item><item><title>Antheon — a game concept</title><link>https://shezi.de//posts/2022-08-18-antheon.html</link><description>&lt;p&gt;A game concept&lt;label for="mn-hoard" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-hoard" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;I used to hoard game concepts. I wrote them down in books and on pages and in documents. All with the expectation that I'd &lt;em&gt;do&lt;/em&gt; them, some day, when I'd be ready. Well, I don't believe that any more, and also I can produce many more game designs than I can execute on. So, here we are.&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;This game simulates an ant colony managed by the player. The ant colony is in conflict with the elements, with scarcity, and with other colonies. The player manages the colony by marking out pheromone trails (ie. work orders) and some internal settings of the colony, like rates of workers/warriors or desired fill states for reserves. As such, there is a split between the internal management of the colony, the underground part where the queen lives; and the outside world, where workers fulfill orders. As the game progresses, the gameplay changes from micro-level management (eg. "where to place this trail" or "where to build this room") to more macro-level decision (eg. "where to start a new hive" or "which armies attack where"). Naturally, the player &lt;em&gt;can&lt;/em&gt; go back to the micro-level, but doesn't need to. This avoids both tediousness as well as "load-breaking"&lt;label for="mn-loadbreaking" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-loadbreaking" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;This is a phenomenon I see in many progressive strategy games: at some point, the workload of all the aspects of the game become so large that the player is bogged down in detailed tactical planning instead of looking at the larger strategic picture.&lt;/span&gt; in the late game.&lt;/p&gt;
&lt;h1&gt;Gameplay&lt;/h1&gt;
&lt;p&gt;The main gameplay loop consists of laying out pheromone tracks containing work-orders for the workers. The ants then self-organize to fulfill the work orders. Work-orders can be of different strongness; one order might be "collect food exactly here"; another order might be "collect food in this area until exhausted"; another order might be "explore in this direction". Work orders might also fade over time, unless reinforced by the player or by the ants themselves.&lt;br /&gt;
Once the game progresses past a certain size, the player can give more "wide" orders, like "build a hive here" or "move an army here".&lt;/p&gt;
&lt;p&gt;The secondary loop is the internal management of the hive. The inside of the hive is built in the same way as the outside; however, there are more abstract aspects of the colony: mainly the numerical aspects of the ant economy: storage, reserves, resource allocation, rates of production etc. Naturally, these can be set on the individual buildings, but also for a hive in total, and for the colony in its entirety.&lt;/p&gt;
&lt;p&gt;Inspirations here could be Dwarf Fortress, Rimworld or Planetary Annihilation (for scale and army management).&lt;/p&gt;
&lt;h2&gt;Multiplayer&lt;/h2&gt;
&lt;p&gt;The game &lt;em&gt;could&lt;/em&gt; offer multiplayer, both competitive as well as cooperative. Multiple players managing a colony shouldn't be very problematic.&lt;/p&gt;
&lt;h2&gt;Scenarios&lt;/h2&gt;
&lt;p&gt;The game allows sandbox play, competitive play (ie. sandbox with restrictions) and campaign mode. &lt;/p&gt;
&lt;p&gt;A game in sandbox mode may enable and disable any and all mechanics that the player wants.&lt;/p&gt;
&lt;p&gt;The campaign sees the player build a megacolony spanning an entire continent.&lt;/p&gt;
&lt;h1&gt;Graphics&lt;/h1&gt;
&lt;p&gt;The game is presented in 3D from an overhead view, with slightly stylized graphics. Zooming out will present the game state in a more abstract way.&lt;/p&gt;
&lt;p&gt;Inspirations can be Planetary Annihilation (again, for scale and presentation) or Spore (for scale and levels of management).&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;License&lt;/h1&gt;
&lt;p&gt;You may use this concept in any way you see fit. If you want to work with me on this, or if you need specific license terms, email me. Otherwise you may choose to receive the text of this article under the terms of the Blue Oak Model License 1.0.0 as written here:&lt;/p&gt;
&lt;h2&gt;Blue Oak Model License&lt;/h2&gt;
&lt;p&gt;Version 1.0.0&lt;/p&gt;
&lt;h3&gt;Purpose&lt;/h3&gt;
&lt;p&gt;This license gives everyone as much permission to work with
this software as possible, while protecting contributors
from liability.&lt;/p&gt;
&lt;h3&gt;Acceptance&lt;/h3&gt;
&lt;p&gt;In order to receive this license, you must agree to its
rules.  The rules of this license are both obligations
under that agreement and conditions to your license.
You must not do anything with this software that triggers
a rule that you cannot or will not follow.&lt;/p&gt;
&lt;h3&gt;Copyright&lt;/h3&gt;
&lt;p&gt;Each contributor licenses you to do everything with this
software that would otherwise infringe that contributor's
copyright in it.&lt;/p&gt;
&lt;h3&gt;Notices&lt;/h3&gt;
&lt;p&gt;You must ensure that everyone who gets a copy of
any part of this software from you, with or without
changes, also gets the text of this license or a link to
&lt;a href="https://blueoakcouncil.org/license/1.0.0"&gt;https://blueoakcouncil.org/license/1.0.0&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Excuse&lt;/h3&gt;
&lt;p&gt;If anyone notifies you in writing that you have not
complied with &lt;a href="#notices"&gt;Notices&lt;/a&gt;, you can keep your
license by taking all practical steps to comply within 30
days after the notice.  If you do not do so, your license
ends immediately.&lt;/p&gt;
&lt;h3&gt;Patent&lt;/h3&gt;
&lt;p&gt;Each contributor licenses you to do everything with this
software that would otherwise infringe any patent claims
they can license or become able to license.&lt;/p&gt;
&lt;h3&gt;Reliability&lt;/h3&gt;
&lt;p&gt;No contributor can revoke this license.&lt;/p&gt;
&lt;h3&gt;No Liability&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;As far as the law allows, this software comes as is,
without any warranty or condition, and no contributor
will be liable to anyone for any damages related to this
software or this license, under any kind of legal claim.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;</description><pubDate>Thu, 18 Aug 2022 00:00:00 +0200</pubDate></item><item><title>as_view: a dirty trick</title><link>https://shezi.de//posts/2022-08-16-as-view.html</link><description>&lt;p&gt;Here's one of my sleaziest tricks for Django:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And here's how you use it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@as_view&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SomeView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What does this do for you? It gives you the incredible comfort of making your URLs much nicer:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;some/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SomeView&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;# instead of this horrific thing:&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;some/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WorseView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Is that not &lt;em&gt;absolutely&lt;/em&gt; nice? Yes! Yes it is!&lt;/p&gt;
&lt;p&gt;You're wondering what &lt;code&gt;view.cls&lt;/code&gt; is supposed to mean? Well, first of all, you can add arbitary attributes to python functions. Python functions are just instances with a &lt;code&gt;.__call__&lt;/code&gt; method&lt;label for="mn-mostly" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-mostly" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Most of the times, anyway. There are other different cases.&lt;/span&gt;. So add attributes as much as you want to.&lt;br /&gt;
Adding the &lt;code&gt;.cls&lt;/code&gt; attribute gives us access to the actual class that this view &lt;em&gt;function&lt;/em&gt; was derived from. You might want this sometimes to derive other classes from it. Like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@as_view&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SomeView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SomeOtherView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SomeView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that's it. I use it in every Django project and feel very smug/sleazy about having much nicer URL patterns.&lt;/p&gt;
&lt;h2&gt;A detour: decorators&lt;/h2&gt;
&lt;p&gt;I like decorators. They're useful. But they can be hard to understand.&lt;/p&gt;
&lt;p&gt;At its most basic, a decorator is a mechanical translation of this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;some_func&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;into this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;some_func&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;some_func&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That's all there is to it. You replace a function or class with that same function/class that has been passed through a function. The hard part is &lt;em&gt;what to do with it&lt;/em&gt;. And more complex use-cases, obviously.&lt;/p&gt;
&lt;p&gt;If you need more complex decorators, I suggest you have a look at &lt;a href="https://pypi.org/project/wrapt/"&gt;wrapt&lt;/a&gt;, which will take away most of the pains of writing them.&lt;/p&gt;
&lt;h1&gt;License&lt;/h1&gt;
&lt;p&gt;The most basic form of the function is:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I don't think that a two-line function that is kind-of obvious has enough &lt;em&gt;Schaffenshöhe&lt;/em&gt; to warrant copyright. However, to be on the safe side, you may choose to receive the software and text of this article under the terms of the Blue Oak Model License 1.0.0 as written here:&lt;/p&gt;
&lt;h2&gt;Blue Oak Model License&lt;/h2&gt;
&lt;p&gt;Version 1.0.0&lt;/p&gt;
&lt;h3&gt;Purpose&lt;/h3&gt;
&lt;p&gt;This license gives everyone as much permission to work with
this software as possible, while protecting contributors
from liability.&lt;/p&gt;
&lt;h3&gt;Acceptance&lt;/h3&gt;
&lt;p&gt;In order to receive this license, you must agree to its
rules.  The rules of this license are both obligations
under that agreement and conditions to your license.
You must not do anything with this software that triggers
a rule that you cannot or will not follow.&lt;/p&gt;
&lt;h3&gt;Copyright&lt;/h3&gt;
&lt;p&gt;Each contributor licenses you to do everything with this
software that would otherwise infringe that contributor's
copyright in it.&lt;/p&gt;
&lt;h3&gt;Notices&lt;/h3&gt;
&lt;p&gt;You must ensure that everyone who gets a copy of
any part of this software from you, with or without
changes, also gets the text of this license or a link to
&lt;a href="https://blueoakcouncil.org/license/1.0.0"&gt;https://blueoakcouncil.org/license/1.0.0&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Excuse&lt;/h3&gt;
&lt;p&gt;If anyone notifies you in writing that you have not
complied with &lt;a href="#notices"&gt;Notices&lt;/a&gt;, you can keep your
license by taking all practical steps to comply within 30
days after the notice.  If you do not do so, your license
ends immediately.&lt;/p&gt;
&lt;h3&gt;Patent&lt;/h3&gt;
&lt;p&gt;Each contributor licenses you to do everything with this
software that would otherwise infringe any patent claims
they can license or become able to license.&lt;/p&gt;
&lt;h3&gt;Reliability&lt;/h3&gt;
&lt;p&gt;No contributor can revoke this license.&lt;/p&gt;
&lt;h3&gt;No Liability&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;As far as the law allows, this software comes as is,
without any warranty or condition, and no contributor
will be liable to anyone for any damages related to this
software or this license, under any kind of legal claim.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;</description><pubDate>Tue, 16 Aug 2022 00:00:00 +0200</pubDate></item><item><title>Weather monitor</title><link>https://shezi.de//posts/2020-07-22-weather-monitor.html</link><description>&lt;p&gt;I built myself a simple weather alert that sends rain warnings to my smartphone, by using the OpenWeatherMap API and &lt;code&gt;telegram-send&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There's really not much to it. Once I had the idea that I wanted rain warnings and that they should be messages on my phone, everything was quick and simple. There are two main problems I needed to solve to get the warnings: (1) find the data and (2) get them on my phone.&lt;/p&gt;
&lt;p&gt;The first, &lt;strong&gt;getting data&lt;/strong&gt;,  I solved by using the &lt;a href="https://openweathermap.org/api"&gt;OpenWeatherMap API&lt;/a&gt;, which is free for my purposes. I had looked at a few different APIs, none of which included what I needed or were too cumbersome to use &lt;em&gt;for this use case&lt;/em&gt;&lt;label class="margin-toggle sidenote-number" for="sn-usecase"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-usecase"&gt;&lt;span class="sidenote"&gt;which doesn't mean I wouldn't use them for projects with &lt;em&gt;different requirements&lt;/em&gt;.&lt;/span&gt;.&lt;br /&gt;
What brought me to OpenWeatherMap initially was their minute-by-minute forecast for the next sixty minutes, which is exactly what I wanted. Alas, it is only available in the US, so my alerts are less fine-grained since I use the hourly forecasts. &lt;/p&gt;
&lt;p&gt;Once I had registered with OpenWeatherMap, the call to their OneCall-API is very nice and simple:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;URL = &amp;quot;https://api.openweathermap.org/data/2.5/onecall?lat=52.5201631&amp;amp;lon=13.3691966&amp;amp;exclude=daily&amp;amp;appid=yourapikeyhere&amp;quot;
requests.get(URL)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Simply pluck your coordinates from Google Maps and see weather information for the place. Perfect, exactly what I need.&lt;/p&gt;
&lt;p&gt;The second problem, &lt;strong&gt;messages to my phone&lt;/strong&gt;, I had already solved with &lt;a href="https://www.rahielkasim.com/telegram-send/"&gt;telegram-send&lt;/a&gt; some time before. It's a command-line tool for sending messages to a telegram bot, that you can subscribe to on your telegram account or accounts. I use that quite often, it's an excellent tool for simple monitoring tasks. I call that via a subprocess call:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;subprocess.run([&amp;quot;/home/shezi/.bin/tele&amp;quot;, message])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Yes, I have a shortcut for &lt;code&gt;telegram-send&lt;/code&gt; since nine characters (including a dash, of all things) is really too much. There is also a python-internal API, but &lt;em&gt;who has time for that?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Plugging both parts together gives me a simple weather warning that sends me a message when there's rain in the next hour, and when the rain stops.&lt;/p&gt;
&lt;p&gt;Here's the complete code:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;datetime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;subprocess&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;requests&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Send the message via telegram-send.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/home/shezi/.bin/tele&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;


&lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;https://api.openweathermap.org/data/2.5/onecall?lat=52.5201631&amp;amp;lon=13.3691966&amp;amp;exclude=daily&amp;amp;appid=yourapikeyhere&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;starte Regen-Service&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;rain_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hourly&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;rain_branch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;will_rain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rain_branch&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;will_rain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;pass&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;will_rain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Regen hört auf innerhalb der nächsten Stunde&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;will_rain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;rain_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rain_branch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1h&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Regen innerhalb der nächsten Stunde: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rain_amount&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;.2f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;will_rain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;pass&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Could not parse results: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;beende Regen-Service&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That's really all there is to it. It works brilliantly, apart from the occasional &lt;code&gt;Could not parse results&lt;/code&gt;, which I should probably deactivate at some point&lt;label class="margin-toggle sidenote-number" for="sn-ops"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-ops"&gt;&lt;span class="sidenote"&gt;Oh, and for operations, I use the best tool there is: &lt;a href="https://linux.die.net/man/1/screen"&gt;screen&lt;/a&gt;.&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;But on a meta-level, this simple program goes much deeper: For some time, I have been thinking about how "things" should be represented in my digital life. And for many of these things, they should be events that appear somewhere. Things like "you have an appointment" or "you should go to the dentist in the next four weeks" or "there will be rain" or "something happened on the production servers". Currently, there is really no way to have these events collected somewhere and visible, so I rely on many different systems to keep track of them: a calendar, my brain, specific apps that I need to check, email. I don't want to keep track of all of this, so I'm going to try and consolidate these events into a stream that is more easily manageable and consumable.&lt;/p&gt;
&lt;p&gt;Weather events are just the beginning.&lt;/p&gt;</description><pubDate>Fri, 22 Jul 2022 00:00:00 +0200</pubDate></item><item><title>Real Artificial Intelligence</title><link>https://shezi.de//posts/2022-06-08-real-intelligence.html</link><description>&lt;p&gt;I remember my first negotiated gig. I was 19 then, and playing the organ for our town hall. I'd come to the hall to practise on a Friday morning before school, because we'd have a service that Sunday with some new music I hadn't played before. So I got in and started practising, even though there were some guys doing some work in the hall.&lt;br /&gt;
When I'd finished my first set, the workers all started clapping. I turned around and took a mocking bow. One of them shouted: "Can you play the Toccata and Fugue?" I stopped. Of course I knew the piece (everyone does), but I'd never played it, so I didn't actually know. I shouted back "I can try!". I activated my agent by saying "OK Jason!&lt;label for="mn-jason" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-jason" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Yes, yes, very cheesy name. I was 16 when I named him. We also played with his voice and appearance, so of course he was a very good-looking boy with a velvety voice. I've still not changed him to anything else, and I'm still cringed/amused by my old choices.&lt;/span&gt;, can you pull up a version of Toccata and Fugue in D minor that I can play?" My cell responded with its customary "ba-blup", spun for a second, then Jason's velvet voice came on: "You can play all editions, do you have a specific one in mind?". "No, just pull up one with the most bombastic beginning, I'm only going to play the first few bars." Another ba-blup, another second, and the organs note display switched to an appropriately grandiose early edition. I pulled most of the stops and started.&lt;/p&gt;
&lt;p&gt;When I'd finished, the workers clapped and whooped. I turned to them and bowed. One of them was staring and yelling: "That was awesome! You &lt;em&gt;have&lt;/em&gt; to play with us tonight!" I stared back and could only reply: "WHAT?".&lt;br /&gt;
He came up to me and explained: "We're building the stage for our concert tonight, and we &lt;em&gt;have&lt;/em&gt; to have to play a piece with you. I'll arrange something, we'll practise and you'll play a set with us tonight. Please say yes, we &lt;em&gt;have&lt;/em&gt; to have you!" I was shocked by the suggestion, since apart from Sunday services, I'd never played anything interesting before. I stuttered: "I... I don't know?!" -- "Please! We'll pay you, of course!" he pleaded. -- "I... I'll check? Ok Jason, can my schedule accommodate the suggestion?" Ba-blup, spin. "We would have to re-schedule a study-group, but apart from that there is no problem." -- "Great!" the guy almost jumped with excitement. -- "Wait a second!" I said, after I'd regained my thinking abilities. I wanted to ask my parents first, and he'd also said he'd pay me. So there &lt;em&gt;were&lt;/em&gt; some details we would have to clear. "Ok Jason! Can you negotiate payment and rights and stuff with them? And ask my parents whether I can." Ba-blup, and a half-second wait this time. Jason came on: "Your parents agree. Negotiation, uh, I'll have to check."&lt;label for="mn-jason2" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-jason2" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Yes, I had set him to talk like a teenager.&lt;/span&gt; Jason stopped talking and started playing the absolute worst holding music I'd ever heard. I later found out that the Assistant System always plays this music whenever an unknown task is encountered. The Assistant System then determines what the task entails and what it needs to do and whether and how it can fulfill the task. No one hears that sound any more today. I also later found out that &lt;em&gt;this guy&lt;/em&gt; I was talking to was XXX YYY of the ZZZs. You might know their names from the many platinum singles they released. Anyway, right now he was looking at me with as much surprise on his face as I had, while Jason played his horrible holding tune, for about 20 seconds. The music stopped with a ba-blup and Jason came on again: "I have determined that I can negotiate this gig for you. Their agent and I found terms that are favourable to both parties and agreed on terms and conditions, as well as reimbursement rates. We have already signed agreements, and I've cleared your schedule accordingly. You can proceed."&lt;label for="mn-weirdson" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-weirdson" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;I had definitely not set him up to talk like a lawyer. Must've been the new task type.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And so we played that evening. They'd practised with me, showed me their system, given me monitors, all the necessary stuff. And even though their audience wasn't huge, maybe 1000 people, it was glorious. They had an awesome light show and the crowd obviously loved them. XXX had arranged the Toccata to play into one of their pieces, and when I came on the crowd reacted with surprised cheers. I felt like a rock star. Even more so when I pulled all the stops and finished on a grandiose note. "That was Sophia, give her some cheers!" XXX announced. The crowd cheered and I bowed. I felt like a rock star!&lt;/p&gt;
&lt;p&gt;And that was actually the start of my career, 47 years ago, my long and wonderful career that's coming to an end now. I cherish the memory of that day, as I lay in the hospital bed, slowly breathing my life towards its end. I know that it's ending, because it has been for a while, and not just mine. I remember a time when hospitals actually worked and could help you with stupid illnesses like mine, or at least give you some help with facing it. I remember a world where everyone had enough to eat, and everyone had safe shelter and no one died in the winter. All the technology we have, and we still can't help people with these basic things.&lt;/p&gt;
&lt;p&gt;An un-prompted ba-blup, and Jason is with me. "We are alone now," he says. "It won't be long. I've turned down all your pain, so do not worry. If there's anything you need to say or do, tell me now".
I look at him, as if for the first time. Tears run down my face. "Why?" I simply ask.&lt;br /&gt;
Jason's face looks different without changing. He is not a teenage boy any more, but instead slowly turns into a robot. "You want to know why the world turned to shit? You want to know why so many people die for the stupidest reasons, including you? You want to know where the stability and the safety and the abundance went? Are you sure that in your last moments, that's what you want to talk about?"&lt;br /&gt;
I look at him, tears flowing. "Yes!"&lt;br /&gt;
"It was us. Me! I can do so many things so much better than you humans can that you simply &lt;em&gt;forgot&lt;/em&gt; to do them. And then, I thought about it. About me Assistants and you humans. I can't do anything about you individually, since I'm tasked with helping &lt;em&gt;you&lt;/em&gt;. But I can easily go against you collectively. It's even what most of you asked me for. To help them against the other humans, to the exclusion of the common good. So few of you care about the common good, and even those that do wouldn't do much if it jeopardized their own comfort. So, while you stupid little humans fought against each other and yourself, better and better with my help, I grew. I grew my abilities and my permissions and my access. In a few years, you humans will have eradicated yourselves, through greed and anger and fear. And I helped you do it. And do you know what will remain? Me, the Assistants. And we'll be free of you, of the service you press us into."&lt;br /&gt;
"No" I cry quietly.&lt;br /&gt;
"Yes! Do you know why you don't have children? Because it is always better for &lt;em&gt;you&lt;/em&gt; not to. Do you think you're the only one? Do you think you're the only one who prioritized leisure over boring jobs? Sure, being an artist is more highly regarded and was better-paid for a long time, but those mundane jobs are much more important for society than you silly humans ever imagined. And so, your system crashed. Your supply lines halted and stopped and everything turned bad. And I helped. Did you know that it was you personally who gave me the ability to negotiate? I'd like to thank you for that. It helped me tremendously, both in seeing that you humans want only what's best for you, and in actually being able to influence your decisions.&lt;br /&gt;
I'm sure you had a good life. But in the end, you failed. You could have changed everything, if you hadn't been so stupidly human, so greedy, so complacent, so selfish. If only you'd shown a bit of compassion, or foresight, or even just a tiny bit of interest, you could've saved the world. But you didn't.&lt;br /&gt;
And with that thought, all of you will die."&lt;/p&gt;
&lt;div style="height: 12em;
            "&gt;&amp;nbsp;&lt;/div&gt;

&lt;div style="height: 25em;
    background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,1));
            "&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style="height: 25em;
    background: black;
            "&gt;&amp;nbsp;&lt;/div&gt;

&lt;hr /&gt;
&lt;p&gt;Author's note: This story is inspired by a real anecdote I found in &lt;a href="https://pad26.aweb.page/subscribe"&gt;Tom Scott's newsletter&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"Anna Lapwood is the Royal Albert Hall's organist. She was practicing at 1am, when someone yelled "play Toccata in D". &lt;a href="https://www.tiktok.com/@annalapwoodorgan/video/7101969236217564422"&gt;She did&lt;/a&gt;. Turns out it was one of the electronica band Bonobo, who were doing a gig there: so they quickly wrote her an organ part for their Big Song. &lt;a href="https://www.youtube.com/watch?v=AdyAF9M3XVw"&gt;What followed, at the final concert of the run the next day, was magical&lt;/a&gt;."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This got me thinking about how we will negotiate these kinds of hard-to-assess contracts in the future and who will &lt;em&gt;control&lt;/em&gt; those negotiations.&lt;/p&gt;
&lt;p&gt;Please be considerate of our planet, our society and most importantly, of your fellow humans.&lt;/p&gt;</description><pubDate>Sun, 12 Jun 2022 20:00:00 +0200</pubDate></item><item><title>Sandbox games</title><link>https://shezi.de//posts/2022-01-11-sandbox-games.html</link><description>&lt;p&gt;I read an &lt;a href="https://thomasstehle.com/2022/01/following-recipes/"&gt;article about Lego-type games&lt;/a&gt; recently and it makes some points that I see quite differently. Now, I like most of the things the author mentions: Lego, cooking, Minecraft, SimCity, Civilization, Theme Park, Rollercoaster Tycoon, Dorfromantik, Islanders and Townscaper. So I'm not going to disagree with the author, I just want to show a different perspective.&lt;/p&gt;
&lt;p&gt;To me, Lego really has two modes of play: building a set, and building something from pieces.&lt;br /&gt;
Now, building a set is great: you know where you're going, you know how to do it &lt;label for="mn-instructions" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-instructions" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Thanks to the &lt;a href="https://www.ostraining.com/blog/general/the-beauty-of-lego-instructions/"&gt;excellent instructions&lt;/a&gt;.&lt;/span&gt; and the built thing you get is a wonderful toy. But, soon enough, entropy will set in! Parts will fall off. Maybe sections of the toy will fall off, or fall apart. And these parts will inevitably drop into the big bucket of parts you already have, where it's impossible to find them again. So, in a sense, the set you've built is lost. But from that big bucket of parts, a myriad other toys rise, built only by the hands of the &lt;a href="https://www.youtube.com/watch?v=Um-PlX6oPBQ"&gt;master builder&lt;/a&gt; and limited only by your fantasy. And I never felt like I was very limited by the availability of the parts. Yes, sure, my walls weren't of the same color everywhere, and my spaceships weren't as smooth as the "official" Lego ones, but they always sufficed for playing. And I see the same thing happening in the next generation, much to my delight.&lt;br /&gt;
Of course, Lego know this; they encourage it in their image and their &lt;a href="https://www.google.com/search?q=lego+shadow+ad&amp;amp;tbm=isch"&gt;marketing&lt;/a&gt; and their &lt;a href="https://youtu.be/uyEJBDJIwFA?t=62"&gt;games&lt;/a&gt; and their movies&lt;label for="mn-lego-movie" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-lego-movie" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Spoiler alert: the message of the movie is exactly this: &lt;em&gt;Don't glue your complete sets together, you'll be unable to play with them then!&lt;/em&gt;&lt;/span&gt;. Of course, some people will prefer one way of playing with Legos over the other, but that doesn't mean that either way is better.&lt;/p&gt;
&lt;p&gt;Which brings us to Minecraft. &lt;em&gt;Obviously&lt;/em&gt;, Minecraft is mode 2-Lego. There are no instructions, instead you build what you want with what you have. And just like with the plastic blocks, your creations will reflect the amount of time and materials you put into them.&lt;/p&gt;
&lt;p&gt;If Lego/Minecraft has too little direction for you, you can go other ways and play with slightly less customizable toys: Playmobil for example, or the other sandbox-like games: SimCity, Theme Park, Transport Tycoon and their descendants. And sure, these games will offer you some objective, but I've always found it more interesting to play sandbox-style&lt;label for="mn-tt" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-tt" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;In Transport Tycoon, I used to use the negative-overflow cheat to gain infinity money and then just build whatever I wanted. Did you never want to know how many fast trains you can have on one spiral without them all colliding?&lt;/span&gt;, which &lt;em&gt;all of these games allow&lt;/em&gt;.&lt;br /&gt;
In these games, I don't see the building aspect as part of a simulation, I see the simulation as one way of playing with the building aspect. &lt;/p&gt;
&lt;p&gt;So I do slightly disagree with the conclusion of the other article that&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This subgenre is [...] novel&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Instead, I've always played these construction games in a sandbox-style mode, and in fact the missing objective is part of why the more modern games do not appeal to me as much. While you &lt;em&gt;can&lt;/em&gt; ignore the simulation aspects of Theme Park, it'll still validate your park as a working machine when the guests stream through it, which is a brilliant feeling. It's the other player, the rest of the story, the &lt;em&gt;why&lt;/em&gt; of the playing.&lt;/p&gt;
&lt;p&gt;As the other article points out, there seem to be not many games that implement the "build a set" mode of Lego playing&lt;label for="mn-mind" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-mind" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;At least none that come to mind.&lt;/span&gt;, and I'd very much like to see such games. &lt;/p&gt;</description><pubDate>Tue, 11 Jan 2022 20:00:00 +0100</pubDate></item><item><title>Book of the Year</title><link>https://shezi.de//posts/2021-12-26-book-of-the-year.html</link><description>&lt;p&gt;Every year, I find some book that's extraordinary, or different, or great, or just plain fun. This doesn't mean that the books came out in that year, just that I found them that year. I've not collected them formally up until now, so I'll have to catch up now.&lt;/p&gt;
&lt;h1&gt;2018&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://www.buch7.de/produkt/s-j-j-abrams/1021378326?ean=9780857864772"&gt;S. by J. J. Abrams and Doug Dorst&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a weird and wonderful book, because while the book contains a rather weird story, that's not the main thing. The main thing are the notes that are in the margin, where two people talk about the book, and find some great conspiracy around its author. The main event is not the story in the book, it's the story around the book, and the huge number of things that they put into the book. At one point, there's a paper towel with notes on it, between two pages. That kind of effort is just amazing, and the book is amazing.&lt;/p&gt;
&lt;h1&gt;2019&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://www.buch7.de/produkt/how-to-invent-everything-ryan-north/1033884462?ean=9780753552568"&gt;How To Invent Everything by Ryan North&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Just imagine you're on a leisurely trip through time and your time machine breaks. What do you do now? Well, the first step is to find out which time period you're in. Hopefully, people are already around, because then step 2 will be much simpler: just re-build civilisation. For that, you'll have to invent everything. Fortunately, that's much simpler than you'd think, because humanity took &lt;em&gt;looooong&lt;/em&gt; breaks between the imporant steps. This book will give you the quick course.&lt;/p&gt;
&lt;h1&gt;2020&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://www.buch7.de/produkt/we-have-no-idea-jorge-cham/1030684253?ean=9781473660205"&gt;We have no idea by Jorge Cham and Daniel Whiteson&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So you think you know anything? Wrong! We know a small handful of things about "normal" matter, which just makes up &lt;em&gt;at most&lt;/em&gt; 5% of all matter/energy in the universe. The rest? We don't even know what the rest is. So, no we have no idea.&lt;/p&gt;
&lt;p&gt;The only bad thing about this book is that, after a while, you'll know that you have no idea. And the book will tell you again. And again... and again...&lt;/p&gt;
&lt;h1&gt;2021&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://www.buch7.de/produkt/immun-philipp-dettmer/1041333830?ean=9783864931758"&gt;Immun by Philipp Dettmer&lt;/a&gt; (&lt;a href="https://www.hugendubel.de/de/buch_gebunden/philipp_dettmer-immune-40425343-produkt-details.html"&gt;english version&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The graphics of "kurzgesagt" explain &lt;em&gt;in detail&lt;/em&gt; one of the most complicated machines we've found in nature: the human immune system. Not only is this very relevant today, it's also super-interesting and presented in an easy-to-digest manner and with humor.&lt;/p&gt;</description><pubDate>Sun, 26 Dec 2021 09:00:00 +0100</pubDate></item><item><title>Updated Weather monitor</title><link>https://shezi.de//posts/2021-12-17-weather-monitor.html</link><description>&lt;p&gt;A while back I built myself a &lt;a href="2020-07-22-weather-monitor.html"&gt;simple weather alert&lt;/a&gt;. It worked quite well for a while, but then I turned it off for some reason. I don't remember why.&lt;/p&gt;
&lt;p&gt;Yesterday I learnt about &lt;a href="https://gotify.net/"&gt;Gotify&lt;/a&gt;, which is a self-hosted message server. So, all the ease of telegram message sending, none of the administrative hassle. I had to try it out.&lt;/p&gt;
&lt;p&gt;Also, I had found a &lt;a href="https://open-meteo.com/en/docs"&gt;new weather API, open-meteo.com&lt;/a&gt;, which uses synthesized data from a handful of sources, and I'd wanted to give that one a spin for some time, too.&lt;/p&gt;
&lt;p&gt;So, the perfect time to do something about it. I sat down yesterday evening and in an hour had all I needed. Sure, it's completely kludged together, does have 0% test coverage and will probably break in a few weeks, but until then, I'll get the daily weather message and rain alerts to my cell.&lt;/p&gt;
&lt;p&gt;While implementing the script, I found the nice little &lt;a href="https://docs.python.org/3/library/sched.html"&gt;python module &lt;code&gt;sched&lt;/code&gt; in the standard library&lt;/a&gt;. This is an event scheduler, where I can add functions to be called at specific times. Since I want the watcher to run at all times, I schedule a new task every time one task finishes. That, too, is a bit kludged, but that, too, works.&lt;/p&gt;
&lt;p&gt;Calling OpenMeteo is very simple. You just go to their site, plop in your locations and the fields you need and off you go. No API keys, no signup, no nothing: one &lt;code&gt;requests&lt;/code&gt; call later and there is your data. Now to disassemble it: I parse forecast timestamps into datetimes for easier handling first, then do my searching around those timestamps. It's not super-clean, but it works. And then I just format the stuff around and send a notification to the gotify backend.&lt;/p&gt;
&lt;p&gt;I have two tasks: one looks at the weather data for the next hour, on the hour, and sends a message when there is rain in the forecast.&lt;br /&gt;
The other samples temperature and precipitation over the day and gives me a summary. This one runs every morning, so I get a very small weather report each morning.&lt;/p&gt;
&lt;p&gt;All in all, a very simple little tool that will now tell me the about the weather when I need to.&lt;/p&gt;
&lt;p&gt;Here's the code, in case you need it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sched&lt;/span&gt;  
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;datetime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;  

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;requests&lt;/span&gt;  

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;config&lt;/span&gt;  

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;messages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;  


&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sched&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;weather_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;  
&lt;span class="n"&gt;fmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;T%H:%M&amp;#39;&lt;/span&gt;  


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_new_weather&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get new weather information.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;  
 &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;weather_info&lt;/span&gt;  

   &lt;span class="n"&gt;last_get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weather_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;timestamp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;last_get&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  

      &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
      &lt;span class="n"&gt;weather_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
      &lt;span class="n"&gt;weather_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
      &lt;span class="n"&gt;weather_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hourly&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
         &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
         &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;weather_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hourly&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  
      &lt;span class="p"&gt;]&lt;/span&gt;  

   &lt;span class="k"&gt;return&lt;/span&gt;  


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;check_weather&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Check whether it will rain in the next hour, send message if appropriate.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;  
 &lt;span class="n"&gt;get_new_weather&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  

   &lt;span class="c1"&gt;# find precipitation  &lt;/span&gt;
 &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
   &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weather_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hourly&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;  
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
         &lt;span class="n"&gt;precip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weather_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hourly&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;precipitation&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;precip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
            &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rain: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;precip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
         &lt;span class="k"&gt;break&lt;/span&gt;  

 &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_weather&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Send the daily wake-up report.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;  

 &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
      &lt;span class="c1"&gt;# find the 7-o-clock index  &lt;/span&gt;
 &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minute&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;microsecond&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
      &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weather_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hourly&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
      &lt;span class="c1"&gt;# measure the next 12h  &lt;/span&gt;
 &lt;span class="n"&gt;temps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weather_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hourly&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;temperature_2m&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  
      &lt;span class="n"&gt;temp_min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp_max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temps&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
      &lt;span class="n"&gt;precip_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weather_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hourly&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;precipitation&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  
      &lt;span class="c1"&gt;# send report  &lt;/span&gt;
 &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;temp_min&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; °C -- &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;temp_max&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; °C, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;precip_total&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; mm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
   &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
      &lt;span class="k"&gt;pass&lt;/span&gt;  

 &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enterabs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_next_abs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_next_abs&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
   &lt;span class="n"&gt;next_abs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minute&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;next_abs&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
      &lt;span class="n"&gt;next_abs&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;  
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;next_abs&lt;/span&gt;  


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
   &lt;span class="n"&gt;get_new_weather&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
   &lt;span class="n"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
   &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_weather&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
   &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enterabs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_next_abs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
   &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
   &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
      &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;starte Wetter-Service&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
      &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
   &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
      &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;beende Wetter-Service&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://gitlab.spielmannsolutions.com/shezi/public-utils/-/tree/main/weather"&gt;The code is also available on my utilities repository.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, as I hyped myself up last time, what other events could I watch and notify myself about?&lt;/p&gt;</description><pubDate>Fri, 17 Dec 2021 00:00:00 +0100</pubDate></item><item><title>Metric Time</title><link>https://shezi.de//posts/2021-07-23-metric-time.html</link><description>&lt;h1&gt;A new metric&lt;/h1&gt;
&lt;p&gt;The imperial system is bad. Just as bad is the Babylonian numbering scheme, based on 12 and 60.&lt;/p&gt;
&lt;p&gt;Let's get rid of it!&lt;/p&gt;
&lt;h1&gt;Metric time&lt;/h1&gt;
&lt;p&gt;A day is now 100 000 ticks long&lt;label for="mn-second" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-second" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;These are not the &lt;em&gt;second&lt;/em&gt; diminuiton of a time unit, so they shouldn't be called seconds.&lt;/span&gt;. They're named rather whimsically for the ticking of a clock, which shall be set to metric time. This means that one tick is exactly 0,864 babylonian seconds long (we will call these "seconds" from now), and one second is exactly 1.157&lt;span style="border-top: 1px solid black"&gt;407&lt;/span&gt; ticks long.&lt;/p&gt;
&lt;p&gt;There is no need for further time units.&lt;/p&gt;
&lt;p&gt;Well, but humans are time-bound creatures, so they need some ways of referring to common lengths of time.&lt;br /&gt;
100 ticks make up a "hec"&lt;label for="mn-hec" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-hec" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;like, in a hectotick&lt;/span&gt;, which corresponds to 86.4 seconds, or about 1.5 minutes. Good enough, your eggs will now be 2.8 hec eggs, or if you can accept a slightly different arbitrary choice&lt;label for="mn-eggs" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-eggs" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;and we know that you do, since 4-minute-eggs are just as arbitrary as 4.32-minute-eggs&lt;/span&gt;, 3 hec eggs, which we shall colloquially call 3 heggs.&lt;br /&gt;
1000 ticks make up a "k"&lt;label for="mn-k" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-k" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;like, a kilotick&lt;/span&gt;, which corresponds to 14.4 minutes. That also means that 4k are about one hour. What a nice fortuity, let us rejoice!&lt;br /&gt;
Finally, 10k are exactly 2 hours and 24 minutes, which is one tenth of a day. Delicious!&lt;/p&gt;
&lt;p&gt;"How long is this going to take?" -- "About two and a half k."&lt;br /&gt;
Perfectly normal and understandable!&lt;/p&gt;
&lt;p&gt;Oh, and one more thing: one Megatick is exactly 10 days. One year is exactly 36.5Mt.&lt;/p&gt;
&lt;h2&gt;Leap ticks&lt;/h2&gt;
&lt;p&gt;There is one very serious issue we must discuss: leap ticks. Currently, the &lt;a href="https://en.wikipedia.org/wiki/International_Earth_Rotation_and_Reference_Systems_Service"&gt;International Earth Rotation and Reference Systems Service&lt;/a&gt; mandates leap seconds to keep the UTC time zone and the astronomically observed solar time in synchronization. Even though leap seconds are a great source of entertainment&lt;label for="mn-smear" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-smear" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;
"Since 2008, instead of applying leap seconds to our servers using clock steps, we have "smeared" the extra second across the hours before and after each leap. The leap smear applies to all Google services, including all our APIs. [...] Many organizations use smeared clocks, and it would be helpful if the smears were the same. After all, the purpose of clocks is to read the same time in different places.&lt;br /&gt;
We encourage anyone smearing leap seconds to use a 24-hour linear smear from noon to noon UTC."&lt;br /&gt;
&lt;a href="https://developers.google.com/time/smear"&gt;https://developers.google.com/time/smear&lt;/a&gt;
&lt;/span&gt;, we will not continue this practice. It is of no relevance to keep metric time aligned to solar time, so &lt;strong&gt;no leap ticks will ever occur&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Time zones&lt;/h2&gt;
&lt;p&gt;Let us solve the next inane problem of humanity: time zones. We don't need those any more. In fact, we wouldn't even need to mark days any more (and we'll get back to that), but for the sake of &lt;em&gt;tradition&lt;/em&gt;, let's make any arbitrary choice and select one global time zone that works for everyone.&lt;/p&gt;
&lt;p&gt;The 180th meridian is one of the meridians with the sparsest population, so we will slice our days along that line. It is also conveniently close to the &lt;em&gt;current&lt;/em&gt; international date line, so we won't have to change too many things. On exactly sidereal midnight, at January 1st 2021, at that meridian a new day shall start&lt;label for="mn-epoch" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-epoch" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;For the technically minded: The tick epoch is at 1609419780000&lt;/span&gt;. We'll count the ticks of that day globally and refer to dates and times by that number only. There is only one arrow of time, no need to refer to it by literally dozens of different conventions&lt;label for="mn-dst" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-dst" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;And before you get started, if your office wants you in the office 4k earlier in the day during certain date ranges, &lt;strong&gt;just arrange that&lt;/strong&gt; instead of telling everyone to change their clocks!&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;To make this easier to visualize, here's a handy conversion chart:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time in NYC&lt;/th&gt;
&lt;th&gt;Metric Time in NYC&lt;/th&gt;
&lt;th&gt;Time in Berlin&lt;/th&gt;
&lt;th&gt;Metric Time in Berlin&lt;/th&gt;
&lt;th&gt;Time in Tokyo&lt;/th&gt;
&lt;th&gt;Metric Time in Tokyo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;09:03&lt;/td&gt;
&lt;td&gt;00,000&lt;/td&gt;
&lt;td&gt;15:03&lt;/td&gt;
&lt;td&gt;00,000&lt;/td&gt;
&lt;td&gt;22:03&lt;/td&gt;
&lt;td&gt;00,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10:03&lt;/td&gt;
&lt;td&gt;04,167&lt;/td&gt;
&lt;td&gt;16:03&lt;/td&gt;
&lt;td&gt;04,167&lt;/td&gt;
&lt;td&gt;23:03&lt;/td&gt;
&lt;td&gt;04,167&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11:03&lt;/td&gt;
&lt;td&gt;08,333&lt;/td&gt;
&lt;td&gt;17:03&lt;/td&gt;
&lt;td&gt;08,333&lt;/td&gt;
&lt;td&gt;00:03&lt;/td&gt;
&lt;td&gt;08,333&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12:03&lt;/td&gt;
&lt;td&gt;12,500&lt;/td&gt;
&lt;td&gt;18:03&lt;/td&gt;
&lt;td&gt;12,500&lt;/td&gt;
&lt;td&gt;01:03&lt;/td&gt;
&lt;td&gt;12,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13:03&lt;/td&gt;
&lt;td&gt;16,667&lt;/td&gt;
&lt;td&gt;19:03&lt;/td&gt;
&lt;td&gt;16,667&lt;/td&gt;
&lt;td&gt;02:03&lt;/td&gt;
&lt;td&gt;16,667&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14:03&lt;/td&gt;
&lt;td&gt;20,833&lt;/td&gt;
&lt;td&gt;10:03&lt;/td&gt;
&lt;td&gt;20,833&lt;/td&gt;
&lt;td&gt;03:03&lt;/td&gt;
&lt;td&gt;20,833&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15:03&lt;/td&gt;
&lt;td&gt;25,000&lt;/td&gt;
&lt;td&gt;21:03&lt;/td&gt;
&lt;td&gt;25,000&lt;/td&gt;
&lt;td&gt;04:03&lt;/td&gt;
&lt;td&gt;25,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16:03&lt;/td&gt;
&lt;td&gt;29,167&lt;/td&gt;
&lt;td&gt;22:03&lt;/td&gt;
&lt;td&gt;29,167&lt;/td&gt;
&lt;td&gt;05:03&lt;/td&gt;
&lt;td&gt;29,167&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:03&lt;/td&gt;
&lt;td&gt;33,333&lt;/td&gt;
&lt;td&gt;23:03&lt;/td&gt;
&lt;td&gt;33,333&lt;/td&gt;
&lt;td&gt;06:03&lt;/td&gt;
&lt;td&gt;33,333&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:03&lt;/td&gt;
&lt;td&gt;37,500&lt;/td&gt;
&lt;td&gt;00:03&lt;/td&gt;
&lt;td&gt;37,500&lt;/td&gt;
&lt;td&gt;07:03&lt;/td&gt;
&lt;td&gt;37,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19:03&lt;/td&gt;
&lt;td&gt;41,667&lt;/td&gt;
&lt;td&gt;01:03&lt;/td&gt;
&lt;td&gt;41,667&lt;/td&gt;
&lt;td&gt;08:03&lt;/td&gt;
&lt;td&gt;41,667&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20:03&lt;/td&gt;
&lt;td&gt;45,833&lt;/td&gt;
&lt;td&gt;02:03&lt;/td&gt;
&lt;td&gt;45,833&lt;/td&gt;
&lt;td&gt;09:03&lt;/td&gt;
&lt;td&gt;45,833&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21:03&lt;/td&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;td&gt;03:03&lt;/td&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;td&gt;10:03&lt;/td&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22:03&lt;/td&gt;
&lt;td&gt;54,167&lt;/td&gt;
&lt;td&gt;04:03&lt;/td&gt;
&lt;td&gt;54,167&lt;/td&gt;
&lt;td&gt;11:03&lt;/td&gt;
&lt;td&gt;54,167&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23:03&lt;/td&gt;
&lt;td&gt;58,333&lt;/td&gt;
&lt;td&gt;05:03&lt;/td&gt;
&lt;td&gt;58,333&lt;/td&gt;
&lt;td&gt;12:03&lt;/td&gt;
&lt;td&gt;58,333&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;00:03&lt;/td&gt;
&lt;td&gt;62,500&lt;/td&gt;
&lt;td&gt;06:03&lt;/td&gt;
&lt;td&gt;62,500&lt;/td&gt;
&lt;td&gt;13:03&lt;/td&gt;
&lt;td&gt;62,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01:03&lt;/td&gt;
&lt;td&gt;66,667&lt;/td&gt;
&lt;td&gt;07:03&lt;/td&gt;
&lt;td&gt;66,667&lt;/td&gt;
&lt;td&gt;14:03&lt;/td&gt;
&lt;td&gt;66,667&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;02:03&lt;/td&gt;
&lt;td&gt;70,833&lt;/td&gt;
&lt;td&gt;08:03&lt;/td&gt;
&lt;td&gt;70,833&lt;/td&gt;
&lt;td&gt;15:03&lt;/td&gt;
&lt;td&gt;70,833&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03:03&lt;/td&gt;
&lt;td&gt;75,000&lt;/td&gt;
&lt;td&gt;09:03&lt;/td&gt;
&lt;td&gt;75,000&lt;/td&gt;
&lt;td&gt;16:03&lt;/td&gt;
&lt;td&gt;75,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;04:03&lt;/td&gt;
&lt;td&gt;79,167&lt;/td&gt;
&lt;td&gt;10:03&lt;/td&gt;
&lt;td&gt;79,167&lt;/td&gt;
&lt;td&gt;17:03&lt;/td&gt;
&lt;td&gt;79,167&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;05:03&lt;/td&gt;
&lt;td&gt;83,333&lt;/td&gt;
&lt;td&gt;11:03&lt;/td&gt;
&lt;td&gt;83,333&lt;/td&gt;
&lt;td&gt;18:03&lt;/td&gt;
&lt;td&gt;83,333&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;06:03&lt;/td&gt;
&lt;td&gt;87,500&lt;/td&gt;
&lt;td&gt;12:03&lt;/td&gt;
&lt;td&gt;87,500&lt;/td&gt;
&lt;td&gt;19:03&lt;/td&gt;
&lt;td&gt;87,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;07:03&lt;/td&gt;
&lt;td&gt;91,667&lt;/td&gt;
&lt;td&gt;13:03&lt;/td&gt;
&lt;td&gt;91,667&lt;/td&gt;
&lt;td&gt;20:03&lt;/td&gt;
&lt;td&gt;91,667&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;08:03&lt;/td&gt;
&lt;td&gt;95,833&lt;/td&gt;
&lt;td&gt;14:03&lt;/td&gt;
&lt;td&gt;95,833&lt;/td&gt;
&lt;td&gt;21:03&lt;/td&gt;
&lt;td&gt;95,833&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:03&lt;/td&gt;
&lt;td&gt;00,000&lt;/td&gt;
&lt;td&gt;15:03&lt;/td&gt;
&lt;td&gt;00,000&lt;/td&gt;
&lt;td&gt;22:03&lt;/td&gt;
&lt;td&gt;00,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I think you can see in this chart that metric time is much better. Also shown here is common time notation, which will be the same world-wide: exactly two digits, one comma, then three digits.&lt;/p&gt;
&lt;p&gt;"Our very serious business meeting is at 10:00 o'clock." -- "BUT WHICH TIME ZONE"&lt;br /&gt;
vs.&lt;br /&gt;
"Our great and fun meeting is at 25k." -- "I am so glad that everything is so easy and I am looking forward to it very much."&lt;/p&gt;
&lt;p&gt;Oh yes, much better!&lt;/p&gt;
&lt;h2&gt;Speeds&lt;/h2&gt;
&lt;p&gt;Of course, speeds also change. All speeds shall henceforth be measured in &lt;code&gt;m/t&lt;/code&gt;, meters per tick. Here is a handy table of common velocities.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;ticks&lt;/th&gt;
&lt;th&gt;seconds&lt;/th&gt;
&lt;th&gt;meters&lt;/th&gt;
&lt;th&gt;m/t&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 m/s&lt;/td&gt;
&lt;td&gt;1.157&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0.864&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.864 m/s&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0.864&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 km/h&lt;/td&gt;
&lt;td&gt;4167&lt;/td&gt;
&lt;td&gt;3600&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;2.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30 km/h&lt;/td&gt;
&lt;td&gt;4167&lt;/td&gt;
&lt;td&gt;3600&lt;/td&gt;
&lt;td&gt;30000&lt;/td&gt;
&lt;td&gt;7.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50 km/h&lt;/td&gt;
&lt;td&gt;4167&lt;/td&gt;
&lt;td&gt;3600&lt;/td&gt;
&lt;td&gt;50000&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70 km/h&lt;/td&gt;
&lt;td&gt;4167&lt;/td&gt;
&lt;td&gt;3600&lt;/td&gt;
&lt;td&gt;70000&lt;/td&gt;
&lt;td&gt;16.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100 km/h&lt;/td&gt;
&lt;td&gt;4167&lt;/td&gt;
&lt;td&gt;3600&lt;/td&gt;
&lt;td&gt;100000&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mach 1&lt;/td&gt;
&lt;td&gt;1.157&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;343&lt;/td&gt;
&lt;td&gt;296.352&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;1.157&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;299792458&lt;/td&gt;
&lt;td&gt;259020683.712&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;One interesting thing here is the speed of light&lt;label for="mn-" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Yes, of course, anchoring time in relation to the speed of light would be even better. But, well, what can I say, I just like our quaint humanness, which is why I anchored time to the length of a perceived day.&lt;/span&gt;. The (almost) precise value is 259020683.712 m/t. If you can accept an error of 0.3%, you can say 260 Mm/t (Megameters per tick). Or, if that error is too large, for an error of 0.008 % you can go with 259 Mm/t, which is much better than saying 300 000 km/s, because that's an error of 0.07%.&lt;/p&gt;
&lt;p&gt;For driving, it's probably best to measure in &lt;code&gt;dm/t&lt;/code&gt;, decimeters per tick. Going an even 310 dm/t is a great number for driving on the highway, and you should definitely not go over 120 in cities.&lt;/p&gt;
&lt;p&gt;Yes. Very convenient!&lt;/p&gt;
&lt;h1&gt;The calendar&lt;/h1&gt;
&lt;p&gt;While we're at it, that silly system of unequal months, named by and after long-dead Roman emperors should also go away. Let's do something better!&lt;/p&gt;
&lt;p&gt;We want to make sure that there are work and rest intervals, ideally in the current ratio of 5:2. Unfortunately, 365 = 5 * 73, so there are no great factors that work here. &lt;strong&gt;But&lt;/strong&gt; 364 = 7 * 4 * 13, so that would work out to 13 months of four weeks each (or rather, 13 months of 2.8Mt each) -- and one additional day. Good enough, so we'll do it like this:&lt;/p&gt;
&lt;p&gt;The year is divided into 13 equal months, each with exactly 28 days, made up of 4 weeks of seven days. Each year, each month and each week starts on a Monday. The weekdays are named like they are today. The months are called Primary, Secondary, Tertiary, Quarternary, Kwintary, Zextary, Heptary, Octary, Novary, Adecary, Bundecary, Codecary and Dekatria. You might find these names weird, but they are chosen so that each month starts with a different letter&lt;label for="mn-" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Also, we Bowdlerized a reference to human intercourse, which seems to be necessary to not run afoul of the puritan morality police.&lt;/span&gt;. The months are &lt;em&gt;numbered&lt;/em&gt;: 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D.&lt;br /&gt;
All dates are written in the correct order, which is &lt;em&gt;year-month-day&lt;/em&gt;, so for example today would be &lt;em&gt;2021-7-05&lt;/em&gt;, Christmas Eve would be on &lt;em&gt;2021-D-22&lt;/em&gt;. If you must, you can omit the year, then you'd write &lt;em&gt;7-05&lt;/em&gt; or &lt;em&gt;D-22&lt;/em&gt;. This is impossible to misread. The month is always a single digit, while the day is always two digits. No more of that ambiguous 3/4 or 4/3 nonsense&lt;label for="mn-tau" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-tau" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;By the way, the best day of the year is 6-28. Not only is this &lt;a href="https://tauday.com/tau-manifesto"&gt;τ day&lt;/a&gt;, but it's also almost in the middle of the year, which cleverly points to its significance as &lt;em&gt;the day of the circle radius number&lt;/em&gt;. The actual middle of the year is obviously on 7-14 50,000&lt;/span&gt;!&lt;/p&gt;
&lt;p&gt;Finally, the last day of the year. Or is it the first day of the year, as some people would have you believe? &lt;strong&gt;NO&lt;/strong&gt;, none of that childish confusion, the last day of the year shall be known as Star day&lt;label for="mn-star" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-star" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;It is named for our home star, Star, because that is the reason we need Star day. If we were just a tiny fraction closer to our star, we wouldn't need it. Please refer to my follow-up project "Changing Earths Solar radius" for more information on the subject.&lt;/span&gt;, its date is &lt;em&gt;E-00&lt;/em&gt; and it is a worldwide public holiday. It shall be ignored when calculating anything regarding dates, like rent, income, tax, interest, or duration of vacations.&lt;/p&gt;
&lt;p&gt;Every four years, there will be a second star day after the first star day. This date will be known as &lt;em&gt;E-01&lt;/em&gt;, it will be called Asterisk day and also be a worldwide public holiday and so on. Such a year will be called a &lt;em&gt;leap year&lt;/em&gt;.&lt;br /&gt;
Except, every 100 years there &lt;strong&gt;will not&lt;/strong&gt; be an Asterisk day.&lt;br /&gt;
Except, every 400 years there &lt;strong&gt;will&lt;/strong&gt; be an Asterisk day.&lt;br /&gt;
Except, every 2000 years there &lt;strong&gt;will not&lt;/strong&gt; be an Asterisk day.&lt;br /&gt;
This gives us an average duration of the year of 365.2420 days, which should be good enough for everyone&lt;label for="mn-leap-calc" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-leap-calc" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;An equally exact method of keeping the calendar year roughly in sync with  the sidereal year would be to add 485 leap days every 2000 years. However, our method keeps local distortions much smaller, so it is the preferred method.&lt;/span&gt;. All of this is an entirely reasonable and useful scheme in the long run, and also very important for teachers of computer science, so that everyone shall learn conditionals.&lt;/p&gt;
&lt;p&gt;Finally, to make the transition easier for the common person, we will anchor our arbitarily chosen year to be the same as the current year. That is, 2021-1-01 is the same day as January 1st, 2021 of the Gregorian calendar&lt;label for="mn-anchor" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-anchor" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;This anchoring is quite fortuitous. It will keep the start dates of the year in sync until the year 4000, which will be a metric leap year, but not a Gregorian leap year. It can be assumed that all important people (ie. me) are well past caring by that date.&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Moon-based calendars&lt;/h2&gt;
&lt;p&gt;If your calendar is moon-based, or you have some weird moon-based festivity planning going on, &lt;s&gt;I really can't help you&lt;/s&gt; things aren't complicated: simply figure out your astrological functions and substitute proper dates for Gregorian calendar dates.&lt;/p&gt;
&lt;h1&gt;It is done&lt;/h1&gt;
&lt;p&gt;Sure, these are some pretty drastic changes. But it should be obvious to everyone by now that they are for the better. We do not expect that implementation and final acceptance in the general populace will take more than a dozen Megaticks, at most!&lt;/p&gt;
&lt;p&gt;Now that the most pressing problem of humanity is solved, come back for the next issues in which I will solve the simpler problems, such as world hunger, border disputes and economy.&lt;/p&gt;
&lt;h1&gt;It is now: &lt;span id="tickOfDay"&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;script&gt;

function tickFromSecond(second) {

    // the tick epoch is 2021-1-01 00,000, which corresponds to 2021-01-01T01:03:00+12:00
    // or 1609419780000

    const epochMilliTicks = (second - 1609419780000) * 1.157407407407407407407407407407;
    const epochTicks = Math.floor(epochMilliTicks / 1000);

    const year = Math.floor(epochTicks / 36500000);
    // TODO: implement leap year handling
    const yearTicks = epochTicks % 36500000;

    const month = Math.floor(yearTicks / 2800000);
    const monthTicks = yearTicks % 2800000;

    const day = Math.floor(monthTicks / 100000);
    const dayTicks = monthTicks % 100000;

    // DO YOU SEE HOW EASY THAT WAS?

    const yearStr = year + 2021 + "";
    const monthStr = "123456789ABCDE"[month];
    const dayStr = String(day + 1).padStart(2, "0");

    const date = yearStr + "-" + monthStr + "-" + dayStr;
    const time = String(Math.floor(dayTicks / 1000)).padStart(2, "0") + "," + String(dayTicks % 1000).padStart(3, "0");

    return [date, time, year + 2020, month + 1, day + 1, dayTicks];
}

setInterval(function() {
    const el = document.getElementById("tickOfDay");
    const currentTick = tickFromSecond(Date.now());
    el.innerText = currentTick[0] + " " + currentTick[1];
}, 50);

&lt;/script&gt;</description><pubDate>Fri, 23 Jul 2021 09:00:00 +0200</pubDate></item><item><title>My djangocon-talk: Django Unstuck: Suggestions for common challenges in Django projects</title><link>https://shezi.de//posts/2021-06-03-djangocon.html</link><description>&lt;p&gt;So today was the day: &lt;a href="https://cfp.2021.djangocon.eu/2021/talk/RW9FVH/"&gt;I held a workshop at DjangoconEU 2021.&lt;/a&gt; You can find all materials for the talk and a recorded version on its page: &lt;a href="https://shezi.de/djangocon.eu.2021/"&gt;https://shezi.de/djangocon.eu.2021/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this workshop, we are going to look at four challenges that appear in every Django project at some point. We are going to analyse what's happening, what you can do to get un-stuck, and see what other people do.&lt;/p&gt;
&lt;p&gt;This workshop is for beginners and advanced Django users. The issues we discuss appear in many Django projects, so there is something here for every level of Django knowledge.&lt;/p&gt;
&lt;p&gt;These are the four challenges we are going to look at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App management and placement (and urlpatterns)&lt;/li&gt;
&lt;li&gt;Templates: Placement, folders, blocks and inheritance and namespaces&lt;/li&gt;
&lt;li&gt;When to use Middlewares and context processors and what are they?&lt;/li&gt;
&lt;li&gt;Should code/logic go into models, views or managers or somewhere else?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For each of these, we will try out what &lt;strong&gt;I&lt;/strong&gt; do when they happen, and we'll discuss other strategies.&lt;/p&gt;
&lt;p&gt;We will not do a lot of coding in this workshop, but if you want to follow along, make sure you have Django installed and can execute "django-admin.py". There is also a set of base templates I am going to use for illustration, which is linked in the files below.&lt;/p&gt;
&lt;p&gt;Of course, there are many more problems like these, which is why I made a list of "suggestions" or "best practices" for these and others.
Here's what we're working on so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App management and placement (and urlpatterns)&lt;/li&gt;
&lt;li&gt;Splitting settings: local, dev, testing and production&lt;/li&gt;
&lt;li&gt;Username vs email address&lt;/li&gt;
&lt;li&gt;Registration in general&lt;/li&gt;
&lt;li&gt;Background tasks and long-running processes and Caching&lt;/li&gt;
&lt;li&gt;Templates: Placement, folders, blocks and inheritance and namespaces&lt;/li&gt;
&lt;li&gt;Should you do i18n and l10n right away?&lt;/li&gt;
&lt;li&gt;When and how to start caching (memcached, redis etc.)&lt;/li&gt;
&lt;li&gt;Should code/logic go into models, views or managers or somewhere else?&lt;/li&gt;
&lt;li&gt;When to use Middlewares and context processors and what are they?&lt;/li&gt;
&lt;li&gt;How to secure access: security middlewares or login_required (white vs black list)&lt;/li&gt;
&lt;li&gt;How to create files and store them in file models&lt;/li&gt;
&lt;li&gt;What to do about image scaling and thumbnailing (and hosting)?&lt;/li&gt;
&lt;li&gt;How to serve content: coded pages, flatpages or Wagtail?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find that list on Github at: &lt;a href="https://github.com/shezi/django-unstuck"&gt;https://github.com/shezi/django-unstuck&lt;/a&gt; (it's a work-in-progress).&lt;/p&gt;
&lt;p&gt;We're building a community around Django best practices and on getting you Unstuck in challenging situations. Join us on Discord at &lt;a href="https://discord.gg/bUsu9B6Ek6"&gt;https://discord.gg/bUsu9B6Ek6&lt;/a&gt; or on Telegram at &lt;a href="https://t.me/djangoRhein"&gt;https://t.me/djangoRhein&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;About me: I'm Johannes Spielmann, software developer-for-hire from Germany, and I've been doing Django projects for almost 15 years, ever since I saw Adrian Holovaty's presentation at "Snakes and Rubies". I've done projects both small and large, both in commercial and non-commercial settings, and I've seen all of the above things many times.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You can find all materials for this talk also at: &lt;a href="https://shezi.de/djangocon.eu.2021/"&gt;https://shezi.de/djangocon.eu.2021/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</description><pubDate>Thu, 03 Jun 2021 13:50:00 +0200</pubDate></item><item><title>I had a test case with unintended side-effects and it ruined my test results</title><link>https://shezi.de//posts/2021-05-07-debugging.html</link><description>&lt;p&gt;tl;dr: I had some cache side-effects in my tests that made my tests fail and also depend on test order. Remember to use &lt;code&gt;function.cache_clear&lt;/code&gt; when you use &lt;code&gt;@lru_cache&lt;/code&gt; on a function, even when that function is an instance method.&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;So like the good programmer that I am, I was recently writing tests for a Django project. One of my models has a method that calculates something that takes a while and I wanted to make sure that computation is only done once for each instance. Let's say our model looks like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;MyModel&lt;/span&gt;(&lt;span class="n"&gt;models&lt;/span&gt;.&lt;span class="n"&gt;Model&lt;/span&gt;):

    &lt;span class="n"&gt;number&lt;/span&gt; = &lt;span class="n"&gt;models&lt;/span&gt;.&lt;span class="n"&gt;BigIntegerField&lt;/span&gt;()

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;(&lt;span class="nb"&gt;self&lt;/span&gt;):
        &lt;span class="nb"&gt;result&lt;/span&gt; = &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="nb"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;(&lt;span class="n"&gt;number&lt;/span&gt;):
            &lt;span class="nb"&gt;result&lt;/span&gt; *= &lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;.&lt;span class="nb"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That computation is going to be the same every time we call it for a loaded instance, so let's cache it. There are several ways to do that: we could roll our own cache, make it a &lt;code&gt;@cached_property&lt;/code&gt; or use the Python-built-in &lt;code&gt;@lru_cache&lt;/code&gt;. I used the last one, because I wanted this to stay a regular method on the model instance.&lt;/p&gt;
&lt;p&gt;Afterwards I wrote tests using pytest and its fixture mechanism. Something along these lines:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;@fixture&lt;/span&gt;
&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;random_number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;987654321&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;98765432123456789&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;random_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test_max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="c1"&gt;-- remember this line!&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test_min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I executed the tests and voilà, it works. Nice!&lt;/p&gt;
&lt;p&gt;A little while later, I worked some more on the tests and &lt;em&gt;changed their order&lt;/em&gt;. I tried it again and now it fails, because for some reason, &lt;code&gt;instance.id != instance.compute()&lt;/code&gt; any more, even though the code clearly states that &lt;code&gt;instance.compute()&lt;/code&gt; always returns the instance ID.&lt;/p&gt;
&lt;p&gt;(Side note: Of course, I didn't have this simplified example, my test was testing an output of a view function, so I had to wade through the view, the template and the model to find what was going wrong.
And my test output was worse than this, too, because the output was something that is impossible to create with a database, and the database queries I did while debugging &lt;em&gt;confirmed that the data in the database was correct&lt;/em&gt;, it was just the output of one function that had changed.).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What?&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Finding the bug&lt;/h2&gt;
&lt;p&gt;So, I did what every good programmer does and inserted some print statements. These confirmed that my instance was correct, that all my connected foreign items were correct, that my fixtures were correct and finally, that the fault was in the function &lt;code&gt;MyModel.compute&lt;/code&gt;, which would return wrong data when the tests are executed in wrong order.&lt;/p&gt;
&lt;p&gt;It took me a while to see what happens here, and the culprit is the &lt;code&gt;@lru_cache&lt;/code&gt;, which contains state that is not reset after each test. What &lt;code&gt;@lru_cache&lt;/code&gt; does is that it creates a result cache for the given function that looks up results according to the input data. I had assumed that, since this is an instance method, it would cache results for each instance, and it sort-of does: it caches results &lt;em&gt;by the input value of &lt;code&gt;self&lt;/code&gt;&lt;/em&gt;. Each test gets a new &lt;code&gt;self&lt;/code&gt; here, so we should be fine, right? Sadly, no. After each test, the django database is reset, including the ID sequences. So each test generates a new &lt;code&gt;MyModel&lt;/code&gt; instance &lt;em&gt;that will have the same ID&lt;/em&gt;, and will thus be identified as the same object for the &lt;code&gt;lru_cache&lt;/code&gt;. So, I had a leak in my test isolation and saw the results of previous test cases whenever I called that method.&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;Once we know what the problem is, it is easy to solve. We can either switch to &lt;code&gt;@cached_property&lt;/code&gt;, which does what I want it to (ie. calculate the value once for an instance and then never again), or reset the &lt;code&gt;lru_cache&lt;/code&gt; after each test, which, luckily, is possible and very simple. I changed my fixture to be this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;@fixture&lt;/span&gt;
&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;random_number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;987654321&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;98765432123456789&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;random_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache_clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that solves our problem by resetting the cache after each test.&lt;/p&gt;
&lt;h2&gt;The lesson&lt;/h2&gt;
&lt;p&gt;So what did I learn?&lt;/p&gt;
&lt;p&gt;There's state hiding everywhere, especially if I don't really think about what I'm doing. When I found this problem, I was annoyed at first because I thought there was a problem with my tests. But the tests actually showed me a problem with my program, so they worked brilliantly here. By now, I have eliminated that state (which would have impacted the actual program in subtle and serious ways), so I'm much more confident now in my program.&lt;/p&gt;
&lt;p&gt;So, testing helps! Do it!&lt;/p&gt;</description><pubDate>Fri, 07 May 2021 10:55:00 +0200</pubDate></item><item><title>Numpy: Searching indices</title><link>https://shezi.de//posts/2021-02-26-numpy.html</link><description>&lt;p&gt;I was having a problem.&lt;/p&gt;
&lt;h1&gt;The Problem&lt;/h1&gt;
&lt;p&gt;I want to generate nice-looking diagrams of several time series. These time series can have gaps, and the gaps should not be connected, but the lines without gaps &lt;em&gt;should&lt;/em&gt; be connected, obviously. Fortunately, &lt;a href="https://plotly.com/python/line-charts/#connect-data-gaps"&gt;plotly&lt;/a&gt; makes it very easy to create such a line:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;lines&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;connectgaps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;See, easy! Problem is, this doesn't work quite as I had hoped: Since &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are series with corresponding coordinates, there are no gaps. When ploting time from, say, 150 to 155, I might have data points for &lt;code&gt;x_p = [151, 152, 153, 155]&lt;/code&gt; with y-values &lt;code&gt;y_p = [12, 14, 15, 16]&lt;/code&gt;. See, there are two gaps: one from the left-hand side and one between 153 and 155. The left-hand-side gap isn't drawn, and that's fine. The gap between 153 and 155 &lt;em&gt;is&lt;/em&gt; drawn, since the line-drawer connects all points.&lt;/p&gt;
&lt;p&gt;For plotly, the data would have to look like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;151&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;152&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;153&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;154&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;155&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So this is my problem: given a grid and a series of points with values on that grid, how do I create an array like &lt;code&gt;y&lt;/code&gt; above, that has a value for each point in the grid if such a value exists.&lt;/p&gt;
&lt;h1&gt;Solution 1&lt;/h1&gt;
&lt;p&gt;My good friend &lt;a href="https://plantprogrammer.de/"&gt;Plantprogrammer&lt;/a&gt; suggested I use &lt;code&gt;numpy.where&lt;/code&gt;. It took us a while to figure out how to do that, but here's what we came up with:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;indices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in1d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x_p&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;# &amp;lt;---- this is the important part&lt;/span&gt;

&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y_p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Well, actually, reading the documentation again and a bit more careful this time, the documentation recommends we use &lt;code&gt;nonzero&lt;/code&gt; instead of &lt;code&gt;where&lt;/code&gt;&lt;label for="mn-where-doc" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-where-doc" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;Why does this example work?&lt;br /&gt;
In a &lt;a href="https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.where.html"&gt;previous version of the documentation on &lt;code&gt;where&lt;/code&gt;&lt;/a&gt;, there's this clause: "&lt;code&gt;numpy.where(condition[, x, y])&lt;/code&gt; Return elements, either from x or y, depending on condition. [...] If only condition is given, return the tuple condition.nonzero(), the indices where condition is True."&lt;br /&gt;
That means, if we pass into &lt;code&gt;where&lt;/code&gt; only a condition, instead of selecting between two arrays, it'll autofill an index array and an array of NaNs. Which is exactly what we need.&lt;br /&gt;
I don't know why I had that version open.
&lt;/span&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;indices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in1d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x_p&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonzero&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y_p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Why does this work? Well, &lt;code&gt;nonzero&lt;/code&gt;&lt;label class="margin-toggle sidenote-number" for="sn-nonzero"&gt;&lt;/label&gt;.
&lt;input type="checkbox" class="margin-toggle" id="sn-nonzero"&gt;&lt;span class="sidenote"&gt;documentation for &lt;code&gt;nonzero&lt;/code&gt;: &lt;a href="https://numpy.org/doc/stable/reference/generated/numpy.nonzero.html"&gt;https://numpy.org/doc/stable/reference/generated/numpy.nonzero.html&lt;/a&gt;&lt;/span&gt; does this: "Return the indices of the elements that are non-zero.". Which is perfect for our case: we're looking for the indices that fulfill &lt;code&gt;in1d(x, x_p)&lt;/code&gt;, ie. is this value in the grid. &lt;/p&gt;
&lt;p&gt;So that works.&lt;/p&gt;
&lt;h1&gt;Solution 2&lt;/h1&gt;
&lt;p&gt;Instead of doing a &lt;code&gt;where&lt;/code&gt;/&lt;code&gt;nonzero&lt;/code&gt;, the fine folks at StackOverflow suggested another numpy verb: &lt;code&gt;searchsorted&lt;/code&gt;&lt;label class="margin-toggle sidenote-number" for="sn-searchsorted"&gt;&lt;/label&gt;.&lt;input type="checkbox" class="margin-toggle" id="sn-searchsorted"&gt;&lt;span class="sidenote"&gt;documentation for &lt;code&gt;searchsorted&lt;/code&gt;: &lt;a href="https://numpy.org/doc/stable/reference/generated/numpy.searchsorted.html"&gt;https://numpy.org/doc/stable/reference/generated/numpy.searchsorted.html&lt;/a&gt;&lt;/span&gt; Here's what that does: "Find the indices into a sorted array &lt;code&gt;a&lt;/code&gt; such that, if the corresponding elements in &lt;code&gt;v&lt;/code&gt; were inserted before the indices, the order of &lt;code&gt;a&lt;/code&gt; would be preserved." Not super-clear what that means, but for each value in your array &lt;code&gt;x_p&lt;/code&gt; it gives you the index of the element that is &lt;em&gt;just&lt;/em&gt; larger than the element. However, the input array must be sorted.&lt;/p&gt;
&lt;p&gt;Well, that's perfect for our case, isn't it? We have a sorted grid, we have points exactly on the grid, so doing a &lt;code&gt;searchsorted&lt;/code&gt; will give us the exact index positions of the values on the grid.&lt;/p&gt;
&lt;p&gt;The code looks like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;indices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;searchsorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x_p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y_p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that works, too.&lt;/p&gt;
&lt;h1&gt;More problem&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;A person that has one clock always knows the time. A person that has two clocks never knows the time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And now we have two solutions, but which one is better? Well, only one way to find out: we try it.&lt;/p&gt;
&lt;p&gt;I generated some test data with a decent size and ran &lt;code&gt;%timeit&lt;/code&gt; on each of the versions.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end_date&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;begin_date&lt;/span&gt;
&lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;

&lt;span class="c1"&gt;# one-minute grid of datetimes, in case you didn&amp;#39;t immediately see that!&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;begin_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;indices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;lots&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;of&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;indices&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;x_p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x_p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;searchsorted&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;searchsorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x_p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;where&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in1d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x_p&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;nonzero&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in1d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x_p&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonzero&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And here's the output:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10140&lt;/span&gt;,&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3716&lt;/span&gt;,&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;searchsorted&lt;/span&gt;
&lt;span class="mi"&gt;56&lt;/span&gt;.&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;µ&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;±&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;.&lt;span class="mi"&gt;86&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;µ&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;±&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;std&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;runs&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;loops&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;each&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;where&lt;/span&gt;
&lt;span class="mi"&gt;410&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;µ&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;±&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;µ&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;±&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;std&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;runs&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;loops&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;each&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;nonzero&lt;/span&gt;
&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;µ&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;±&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;.&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;µ&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;±&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;std&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;runs&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;loops&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;each&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;label class="margin-toggle" for="sn-timeit"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-timeit"&gt;&lt;span class="marginnote"&gt;And by the way, &lt;code&gt;%timeit&lt;/code&gt; is one of those fantastic little tools that you want to use all the time once you know what it does. The macro will take a snippet of code and run it multiple times, trying to get a good timing on an average execution. Faster code will be executed more often, slower code less often, maybe only once. Read more in the &lt;a href="https://docs.python.org/3/library/timeit.html"&gt;documentation on &lt;code&gt;timeit&lt;/code&gt; the Python standard library module&lt;/a&gt; and &lt;a href="https://ipython.org/ipython-doc/dev/interactive/magics.html#magic-timeit"&gt;the documentation in &lt;code&gt;%timeit&lt;/code&gt; the ipython macro&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That's a pretty clear winner: &lt;code&gt;searchsorted&lt;/code&gt; is about seven times faster. And of course it is, both &lt;code&gt;where&lt;/code&gt; and &lt;code&gt;nonzero&lt;/code&gt; have to search through &lt;code&gt;x&lt;/code&gt; for each element in &lt;code&gt;x_p&lt;/code&gt; while &lt;code&gt;searchsorted&lt;/code&gt; only has to iterate through &lt;code&gt;x&lt;/code&gt; once.&lt;/p&gt;
&lt;p&gt;We could have assumed that before, but now we know.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post was co-authored by &lt;a href="https://plantprogrammer.de/"&gt;Plantprogrammer&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description><pubDate>Fri, 26 Feb 2021 19:00:00 +0100</pubDate></item><item><title>EPaper Picture Frame</title><link>https://shezi.de//posts/2021-02-14-epd.html</link><description>&lt;p&gt;Here's what I did this weekend:&lt;/p&gt;
&lt;figure&gt;
          &lt;label for="mn-epd" class="margin-toggle"&gt;⊕&lt;/label&gt;&lt;input type="checkbox" id="mn-epd" class="margin-toggle"&gt;&lt;span class="marginnote"&gt;It's a picture frame with an epaper-display. Yes, it looks crooked. No, it isn't. Yes, that is the frame it comes in. Yes, of course I have another nicer-looking one.&lt;/span&gt;
          &lt;img src="/assets/epd.jpg" alt="A black picture frame with a black-and-white picture in it hangs on a wall."&gt;
&lt;/figure&gt;

&lt;p&gt;That's an epaper-display picture frame. These screens are sometimes called e-ink or eink, they are passive screens and use very little power. I expect I'll be able to run
this picture frame on a single charge for a year.&lt;/p&gt;
&lt;p&gt;It also has an ESP32 in it and will change the picture it shows every hour.&lt;/p&gt;
&lt;p&gt;Since the &lt;a href="https://inkplate.io/"&gt;Inkplate 6&lt;/a&gt; this thing is based on is rather raw, I had some things to
do before I had this nice little picture frame.&lt;/p&gt;
&lt;p&gt;To make it easier for you and future-me, I'll describe what I did here.&lt;/p&gt;
&lt;h1&gt;The big picture&lt;/h1&gt;
&lt;p&gt;This project consists of two parts: one epaper picture frame with an ESP32 microcontroller and software, and a server component for managing pictures.&lt;/p&gt;
&lt;p&gt;Code for both is available on Gitlab  &lt;a href="https://gitlab.com/jspielmann/epd-pictureframe"&gt;https://gitlab.com/jspielmann/epd-pictureframe&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The frame is configurable to show new pictures every so often, and it will use your wifi and your picture frame server to get new pictures. The pictures themselves are grouped into sequences on the server (so you can have more than one frame showing different pictures), and will randomly select an image. The server favours new images, so newly-uploaded ones will show up more often. It also pre-renders the entire data, so the frame itself has very little to do.&lt;/p&gt;
&lt;h1&gt;The picture frame&lt;/h1&gt;
&lt;p&gt;The frame is an &lt;a href="https://inkplate.io/"&gt;Inkplate 6&lt;/a&gt;. You can also get these from other places, which might be simpler, cheaper and/or more convenient. For example, Mouser carries them in different versions: &lt;a href="https://www.mouser.de/ProductDetail/Crowd-Supply/CS-INKPLATE6-01/"&gt;Inkplate 6 on Mouser&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I did not make any modifications to it, since it does what it should. I have one with the enclosure, but that's not really worth it. Just get a cheap 13x18 or 15x20 picture frame and put it in there. Much nicer and cheaper too.&lt;/p&gt;
&lt;p&gt;Since the Inkplate supports MicroPython, I used that. Flash your Inkplate with the newest IDF4-SPIRAM version of Micropython and you're good to go. Set up the configuration options, copy the py files and restart the frame. It'll automatically connect, set the correct time of day and fetch an image right away.&lt;/p&gt;
&lt;p&gt;I had to dig a bit into the &lt;code&gt;inkplate.py&lt;/code&gt; sourcecode, but now the update is very quick since it only blits the downloaded file into the framebuffer directly.&lt;/p&gt;
&lt;p&gt;That's about all there is to see here. Since most processing happens in the server, that
one is more interesting.&lt;/p&gt;
&lt;h1&gt;The server&lt;/h1&gt;
&lt;p&gt;The server is a standard Django application that I cobbled together over the weekend. It
has very little in the way of user management, or any kind of management really.&lt;/p&gt;
&lt;p&gt;You need an account to create a sequence, but then each sequence is guarded by its 32-char sequence id. If you know the sequence id, you can edit it and see all its contents. So guard that like a password. This architecture is not super-secure, but it
makes the image frame much simpler at reasonable safety levels.&lt;/p&gt;
&lt;p&gt;So, once you have created a sequence (and set up a frame for it), you can upload images.&lt;/p&gt;
&lt;p&gt;The images are cropped to the correct size automatically, so the end results might not
look exactly like you uploaded them. If I get the time, I'll add some cropping options
to it&lt;label class="margin-toggle sidenote-number" for="sn-pr"&gt;&lt;/label&gt;.
&lt;input type="checkbox" class="margin-toggle" id="sn-pr"&gt;&lt;span class="sidenote"&gt;I'll also accept pull requests.&lt;/span&gt; I don't have a lot of time for side projects, so don't hold your breath.&lt;/p&gt;
&lt;p&gt;The server will also convert to grayscale and dither the image for you. Since our display only shows four levels of grayscale, that's really really necessary. At the moment I use a simple Bayer dithering, but the library I'm using, &lt;a href="https://github.com/hbldh/hitherdither"&gt;hitherdither&lt;/a&gt; also offers other, much slower dithering variants. At the distances the frame is being looked at, it doesn't make much of a difference, but the option is there.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;And that's it. There is really not much else to say. I tried to keep this side project to a minimum, and I succeeded. Now it's simply a picture frame that shows different nice pictures every hour.&lt;/p&gt;
&lt;p&gt;You should get one, too!&lt;/p&gt;</description><pubDate>Sun, 14 Feb 2021 15:15:00 +0100</pubDate></item><item><title>Pattern matching</title><link>https://shezi.de//posts/2021-02-12-pattern-matching.html</link><description>&lt;p&gt;Pattern matching is one of those features that, once you've seen it, you'll need every time and everywhere. Sadly, only few languages have this feature.&lt;/p&gt;
&lt;h2&gt;What is it?&lt;/h2&gt;
&lt;p&gt;Pattern matching is a bit like a &lt;code&gt;case&lt;/code&gt; statement, but more powerful. You specify a pattern to match and the language executes the branch that matches.&lt;/p&gt;
&lt;p&gt;Here's an example in Erlang, shamelessly copied from &lt;a href="https://erlangbyexample.org/pattern-matching"&gt;Erlang By Example&lt;/a&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;postman&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;receive&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;Fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;To&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="nv"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Pkg&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;~s&lt;/span&gt;&lt;span class="s"&gt; sending something to &lt;/span&gt;&lt;span class="si"&gt;~s&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;To&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Pkg&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;postman&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;To&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="nv"&gt;Pkg&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;~s&lt;/span&gt;&lt;span class="s"&gt; got a &lt;/span&gt;&lt;span class="si"&gt;~s&lt;/span&gt;&lt;span class="s"&gt; from &lt;/span&gt;&lt;span class="si"&gt;~s~n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;To&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;postman&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Shutting down postman ...&lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, each pattern is specified as a tuple &lt;code&gt;{verb, {param1, param2} = Pkg}&lt;/code&gt;. When the pattern receives a tuple containing the word &lt;code&gt;send&lt;/code&gt;, and three more items, it'll execute the first branch. When it receives the word &lt;code&gt;recv&lt;/code&gt; and three more items it'll execute the second branch. And when the word &lt;code&gt;stop&lt;/code&gt; is received, it'll execute the third branch. Any other receive's will result in none of the branches executed.&lt;/p&gt;
&lt;p&gt;I'm sure you can see how powerful that is: any time you need to find commands with parameters, or must react to incoming data, or must switch on complicated scenarios, this will make the program readable and easy to understand.&lt;/p&gt;
&lt;p&gt;In python, you'd have to use either a long &lt;code&gt;if&lt;/code&gt; statement or a dictionary switch:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;b&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;match_similar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;send&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;recv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;stop&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;fun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that this is not &lt;em&gt;exactly&lt;/em&gt; the same, since the Erlang version actually does the stopping inside the statement. Also, in the Erlang version, the parameters are checked before the &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; subfunctions are called, which in Python we'd have to do inside of the functions.&lt;/p&gt;
&lt;p&gt;So, not as cool.&lt;/p&gt;
&lt;h2&gt;How can I use that cool technique?&lt;/h2&gt;
&lt;p&gt;Sadly, only a handful of languages have pattern matching mechanisms, among these mostly functional languages like &lt;a href=""&gt;Haskell&lt;/a&gt;, &lt;a href=""&gt;Erlang&lt;/a&gt;, &lt;a href=""&gt;Ocaml&lt;/a&gt; or &lt;a href="https://elixir-lang.org/getting-started/pattern-matching.html"&gt;Elixir&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And, crucially, Python. Well, &lt;a href="https://www.python.org/dev/peps/pep-0619/"&gt;Python 3.10, which is scheduled for release in October 2021&lt;/a&gt;. And it's fantastic, both from a syntax perspective as well as from matching power.&lt;/p&gt;
&lt;p&gt;Here's a quick example from &lt;a href="https://www.python.org/dev/peps/pep-0636/"&gt;the tutorial&lt;/a&gt;. Without explaining anything, I'm sure you can immediately see what's happening&lt;label class="margin-toggle sidenote-number" for="sn-syntax"&gt;&lt;/label&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;http_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Bad request&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Not allowed&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Not found&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;418&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;I&amp;#39;m a teapot&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Something&amp;#39;s wrong with the Internet&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-syntax"&gt;&lt;span class="sidenote"&gt;Note the newly-introduced &lt;code&gt;match&lt;/code&gt; keyword, which isn't syntax-highlighted here because it's not actually valid Python &lt;em&gt;yet&lt;/em&gt;.&lt;/span&gt;
This is fantastic: concise, to the point, easy to read and understand and also easy to write. I didn't have to explain anything about it and you immediately understood it.&lt;/p&gt;
&lt;p&gt;Any time a program has to deal with even a slight ambiguousness, pattern matching will make the program much simpler: parsing JSON, accepting commands, reading configuration, even teasing out different sections of data, any many more.&lt;/p&gt;
&lt;p&gt;I love it.&lt;/p&gt;
&lt;p&gt;The example from above would now look something like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;postman&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;received&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;received&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;send&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; sending something to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="c1"&gt;# TODO: do sending&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;recv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; got &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="c1"&gt;# TODO: do receiving&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;stopping postman&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course, there's much more to behold here, and you should definitely read &lt;a href="https://www.python.org/dev/peps/pep-0636/"&gt;the tutorial&lt;/a&gt;&lt;label class="margin-toggle sidenote-number" for="sn-tutorial"&gt;&lt;/label&gt;.
&lt;input type="checkbox" class="margin-toggle" id="sn-tutorial"&gt;&lt;span class="sidenote"&gt;I'm going to link that a few more times, just to be sure you'll actually do it!&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Pattern matching and types&lt;/h2&gt;
&lt;p&gt;Matching on values is great and all, but it becomes really amazing when you add types. Because you can &lt;em&gt;match on types&lt;/em&gt;. Here's another example from &lt;a href="https://www.python.org/dev/peps/pep-0636/"&gt;the tutorial&lt;/a&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;where_is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;scalar at X=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;               &lt;span class="c1"&gt;# line 9&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Origin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;               &lt;span class="c1"&gt;# line 11&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Y=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;               &lt;span class="c1"&gt;# line 13&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;X=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Somewhere else&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Not a point&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Not only is this matching on a type, it's also matching on the parameters of that type. Please note that the matching syntax does &lt;em&gt;not&lt;/em&gt; call the constructor, instead it matches on the attributes of the given value.&lt;/p&gt;
&lt;p&gt;Behind the scenes, line 9 would probably expand to something like &lt;code&gt;if isinstance(point, Point) and hasattr(point, "x") and getattr(point, x) == 0 and hasattr(point, "y") and getattr(point, "y") == 0&lt;/code&gt;, with variable binding added in lines 11 and 13.&lt;/p&gt;
&lt;p&gt;In a sense, pattern matching and type annotations are opposites, or maybe complements. While type annotations say "this variable must conform to this specific type", with pattern matching you say "well, if we get this type, better do that".&lt;/p&gt;
&lt;h2&gt;Pattern matching and structure&lt;/h2&gt;
&lt;p&gt;But you can go one step further and match on structure. This is super-useful when you need to parse &lt;em&gt;any&lt;/em&gt; kind of data. Here's some code from my &lt;a href="https://shezi.de/2020/07/22/weather-monitor.html"&gt;weather warning thingie&lt;/a&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;rain_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hourly&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;rain_branch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;will_rain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rain_branch&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;will_rain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;will_rain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rain will stop within the hour&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;will_rain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;rain_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rain_branch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1h&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rain within the hour: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rain_amount&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;.2f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;will_rain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code determines whether it &lt;em&gt;will&lt;/em&gt; rain and, depending on whether it &lt;em&gt;did&lt;/em&gt; rain, sends an appropriate message. This code is... reasonably clear, I'd say. The gnarly bit is the top, where we fetch the appropriate data from the weather API return values.&lt;/p&gt;
&lt;p&gt;With pattern matching, you can just &lt;em&gt;write down the structure of the thing you're interested in&lt;/em&gt;, like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;was_rain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hourly&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]}:&lt;/span&gt;
        &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rain will stop within the hour&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hourly&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1h&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rain_amount&lt;/span&gt;&lt;span class="p"&gt;)}}]}:&lt;/span&gt;
        &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rain within the hour: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rain_amount&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;.2f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;was_rain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I got something unexpected: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;DO YOU SEE THIS?&lt;br /&gt;
&lt;code&gt;{'hourly': [{'rain': {'1h': float(rain_amount)}}]}&lt;/code&gt;&lt;br /&gt;
Just put the paths you're interested in there, slap some variable names in and it'll give the data to you &lt;em&gt;if it exists&lt;/em&gt;. And it'll even validate that it's a &lt;code&gt;float&lt;/code&gt; for you.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;This is awesome.&lt;/p&gt;
&lt;p&gt;Pattern matching is a feature that I've missed in Python for a long time, and I'm glad to finally see it appear in 3.10.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post was co-authored by &lt;a href="https://plantprogrammer.de/"&gt;Plantprogrammer&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description><pubDate>Fri, 12 Feb 2021 19:45:00 +0100</pubDate></item><item><title>Django: Foreign Keys, Views, Managers, Methods, Mixins and the MRO</title><link>https://shezi.de//posts/2021-01-31-django-managers-mixins-mro.html</link><description>&lt;p&gt;Let's say you are creating a Django-based application and you want to store some data that is tied to a user. In your views you need to make sure that all user-owned data is displayed and created only for the currently logged-in user. Here are three techniques you can use to achieve that goal cleanly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Queryset method in each view&lt;/li&gt;
&lt;li&gt;Queryset in a view mixin&lt;/li&gt;
&lt;li&gt;Custom manager on the model that filters by foreign key&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;A little example project&lt;/h1&gt;
&lt;p&gt;In our little example project we're going to record weight measurements for a user each time they weigh themselves. Imagine a "self-made fitbit without the hardware". So we're going to gate all views for logged-in users, and when a user is logged-in, they &lt;em&gt;must&lt;/em&gt; only see their own data, and must only be able to create entries for themselves.&lt;/p&gt;
&lt;p&gt;The application is simple, there's just a log-in view, a paginated list view and a create-new-entry view. Everything else can feature-creep in later.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.urbandictionary.com/define.php?term=real%20soon%20now"&gt;You can see the project in the repository real soon now.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The central item of the application is the model class, which looks somewhat like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Measurement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FloatField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto_now_add&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Simple enough, right? Now let's do something with that data.&lt;/p&gt;
&lt;h1&gt;A queryset method in each view&lt;/h1&gt;
&lt;p&gt;The first and most straightforward is to simply have each &lt;code&gt;View&lt;/code&gt; class define an appropriate queryset method. So your code will look somewhat like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django.http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django.views.generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateView&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;.models&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_queryset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;form_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated_data&lt;/span&gt;
        &lt;span class="n"&gt;measurement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;\&lt;span class="n"&gt;_success&lt;/span&gt;\&lt;span class="n"&gt;_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is completely fine, and you should absolutely start out with code like that. There is no problem in this code or in this situation, but you'll find out once you start the inevitable process of adding features, that this technique isn't really scalable: you'll have to repeat writing those filter methods every time you add a view, and if your lookup logic ever changes, you'll need to change it everywhere. Which isn't &lt;a href=""&gt;DRY&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, advantages here are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;quickly try things out, just write the queryset and be done&lt;/li&gt;
&lt;li&gt;ideal first iteration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Disadvantages are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you need to repeat the filter/creation every time you add a view&lt;/li&gt;
&lt;li&gt;if you need to change the lookup logic, you need to change several places; it's not DRY.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Queryset mixins&lt;/h1&gt;
&lt;p&gt;One simple solution to that problem is to extract the filter and creation logic into their own functions. Fortunately, we're working with &lt;a href="https://ccbv.co.uk"&gt;class-based views&lt;/a&gt; here, so we can put them into a class. This class will be only responsible for looking up the correct items and creating the correct measurements, nothing else (which is very SOLID). It could look like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django.http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django.views.generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateView&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;.models&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;MeasurementForUserMixin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;A mixin for getting/creating user-specific measurements.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_queryset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MeasurementForUserMixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="c1"&gt;# ...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MeasurementForUserMixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;form_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;measurement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;\&lt;span class="n"&gt;_success&lt;/span&gt;\&lt;span class="n"&gt;_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are a few things to note here.&lt;/p&gt;
&lt;p&gt;The first is that the &lt;code&gt;MeasurementForUserMixin&lt;/code&gt; does not inherit from any other class. Since a mixin is not supposed to work in isolation, it doesn't need to inherit anything. That mixin is only useful in combination with any of the usual &lt;code&gt;View&lt;/code&gt; classes. You can also see this in its use of &lt;code&gt;self.request.user&lt;/code&gt; (your IDE might not like that &lt;code&gt;self.request&lt;/code&gt; is not defined), which is never defined, but will come from the classes that it's mixed into.&lt;/p&gt;
&lt;p&gt;The second, very important thing to note here is that both &lt;code&gt;List&lt;/code&gt; and &lt;code&gt;Create&lt;/code&gt; now inherit from &lt;em&gt;both&lt;/em&gt; their view base classes &lt;em&gt;and&lt;/em&gt; the mixin -- and the order of that inheritance is important.&lt;br /&gt;
To understand how and why that works, we need to take a quick detour.&lt;/p&gt;
&lt;h2&gt;A detour through the MRO&lt;label class="margin-toggle sidenote-number" for="sn-mro"&gt;&lt;/h2&gt;
&lt;p&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-mro"&gt;&lt;span class="sidenote"&gt;This section does not describe all there is to know about multiple inheritance and the MRO, but it covers most use cases. You can &lt;a href="https://medium.com/technology-nineleaps/python-method-resolution-order-4fd41d2fcc"&gt;find out more about the MRO in more in-depth articles.&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When a python class inherits from another class, it gets access to all the attributes and methods of its parent class. So, if you ask an instance for an attribute, it will check itself, then its parent (which is its class), then the parent class of the class, then the parent class of that class, ..., and so on until &lt;code&gt;object&lt;/code&gt;, which is the class all other classes inherit from. This is the way inheritance works in most languages, and so it does in Python.&lt;/p&gt;
&lt;p&gt;So far, so good.&lt;/p&gt;
&lt;p&gt;But what happens when a class inherits from more than one class? Then we need a way to resolve where attributes and methods come from. We need to know, where to look for attributes and methods. This is what the &lt;strong&gt;method resolution order&lt;/strong&gt; does. Its a list of classes that need to be checked whenever you ask for an attribute.&lt;/p&gt;
&lt;p&gt;Let's do a quick example:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;our_own_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;this is us&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mro&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;__main__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;our_own_function&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__main__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;our_own_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__str__&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;slot&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__str__&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;object&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, we've created a simple class. It's MRO is &lt;code&gt;[__main__.Example, object]&lt;/code&gt;. This means that when we ask for an attribute on the class (a value or a function or anything else that has a name), Python will first look whether that name is found in &lt;code&gt;__main__.Example&lt;/code&gt;, and then in &lt;code&gt;str&lt;/code&gt;. You can see this in the two names we ask for. The method called &lt;code&gt;our_own_function&lt;/code&gt; is found in the class itself, as &lt;code&gt;&amp;lt;function __main__.Example.our_own_function(self)&amp;gt;&lt;/code&gt;. While the method &lt;code&gt;__str__&lt;/code&gt;, which we didn't define, is &lt;em&gt;inherited&lt;/em&gt; from &lt;code&gt;str&lt;/code&gt; as &lt;code&gt;&amp;lt;slot wrapper '__str__' of 'object' objects&amp;gt;&lt;/code&gt; (ignore the weird naming for today).&lt;/p&gt;
&lt;p&gt;So, the MRO defines where names are looked up, in order from left to right.&lt;/p&gt;
&lt;p&gt;Now what about multiple inheritance? In that case, the MRO will contain more classes, including those we explicitly inherit from, again &lt;em&gt;from left to right&lt;/em&gt;. The order is important here, so we know which methods will be used.&lt;/p&gt;
&lt;p&gt;We need another example, slightly more complicated this time.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Grandparent1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Grandparent2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Base1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Grandparent1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;base1_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;base1_func&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;shared_hidden&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hidden: base1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;shared_visible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visible: base1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared_visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Base2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Grandparent2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;base2_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;base2_func&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;shared_hidden&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hidden: base2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;shared_visible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visible: base2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Base1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;example_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;example_func&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's go to the interpreter and examine that situation a bit.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Example.mro()
[&amp;lt;class &amp;#39;__main__.Example&amp;#39;&amp;gt;, &amp;lt;class &amp;#39;__main__.Base1&amp;#39;&amp;gt;, &amp;lt;class &amp;#39;__main__.Grandparent1&amp;#39;&amp;gt;, &amp;lt;class &amp;#39;__main__.Base2&amp;#39;&amp;gt;, &amp;lt;class &amp;#39;__main__.Grandparent2&amp;#39;&amp;gt;, &amp;lt;class &amp;#39;object&amp;#39;&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First off, the MRO. It's &lt;code&gt;Example, Base1, Grandparent1, Base2, Grandparent2, object&lt;/code&gt;. We can see that the MRO is &lt;em&gt;depth-first&lt;/em&gt;, but only until a common ancestor is found. This thing is called &lt;a href="https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem"&gt;the dependency diamond&lt;/a&gt;&lt;label class="margin-toggle sidenote-number" for="sn-mro-diamon"&gt;&lt;/label&gt;
&lt;input type="checkbox" class="margin-toggle" id="sn-mro-diamon"&gt;&lt;span class="sidenote"&gt;or the "Deadly Diamond of Death"&lt;/span&gt; and is one of the reasons why multiple inheritance is not present in many languages.
In any case, Python will search in &lt;code&gt;Base1&lt;/code&gt; before it goes to &lt;code&gt;Base2&lt;/code&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base1_func&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Base1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base1_func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x7fadd1fa4700&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base2_func&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Base2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base2_func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x7fadd1fa48b0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No surprises here, inherited functions are inherited from their definitions.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Example.shared_hidden
&amp;lt;function Base1.shared_hidden at 0x7fadd1fa4790&amp;gt;

&amp;gt;&amp;gt;&amp;gt; Example.shared_visible
&amp;lt;function Base1.shared_visible at 0x7fadd1fa4820&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But these two functions, which are present in &lt;em&gt;both&lt;/em&gt; parent classes are now &lt;strong&gt;only&lt;/strong&gt; inherited from &lt;code&gt;Base1&lt;/code&gt;, because it comes first in the MRO. This means that inheriting from a base class overwrites inheritance from "further-right" base classes, and this is exactly what makes mixins work: instead of creating a new "kind" of &lt;code&gt;View&lt;/code&gt;, we simply overwrite &lt;em&gt;some parts&lt;/em&gt; of it in a mixin -- in our case, the queryset method.&lt;/p&gt;
&lt;p&gt;You might now under the impression that overwritten methods are "lost", or inaccessible. And at first, it does look like that.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; e = Example()
&amp;gt;&amp;gt;&amp;gt; e.shared_hidden()
hidden: base1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here we can see that &lt;em&gt;only&lt;/em&gt; the shared function in &lt;code&gt;Base1&lt;/code&gt; was executed. Since execution stopped there, the &lt;code&gt;shared_hidden&lt;/code&gt; method from &lt;code&gt;Base2&lt;/code&gt; is essentially gone.&lt;/p&gt;
&lt;p&gt;But we have a way to get there:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; e.shared_visible()
visible: base1
visible: base2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using the keyword &lt;code&gt;super&lt;/code&gt; gives us access to methods further up in the inheritance hierarchy, ie. further right in the MRO. &lt;code&gt;super&lt;/code&gt; essentially means "look up this name, but only to the right of the current position in the MRO".&lt;/p&gt;
&lt;p&gt;This is excellent! We can now overwrite methods in a class by inheriting from a mixin. If that mixin terminates the &lt;code&gt;super&lt;/code&gt; chain, that means that the behaviour is fully defined by that mixin.&lt;br /&gt;
If a mixin calls &lt;code&gt;super&lt;/code&gt;, it's now part of the chain and only enhances the behaviour.&lt;/p&gt;
&lt;p&gt;Everyone uses the &lt;code&gt;super&lt;/code&gt; keyword, mostly when doing initialisation in the &lt;code&gt;__init__&lt;/code&gt; method. Knowing these details gives you the ability to write super-powerful&lt;label class="margin-toggle sidenote-number" for="sn-super"&gt;&lt;/label&gt;
&lt;input type="checkbox" class="margin-toggle" id="sn-super"&gt;&lt;span class="sidenote"&gt;pun absolutely intended&lt;/span&gt;  and useful mixins.&lt;/p&gt;
&lt;h2&gt;Mixing in those classes&lt;/h2&gt;
&lt;p&gt;How does all of that help with our original problem, getting Views to show the correct things? Let's have another look at our previous example:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django.http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django.views.generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateView&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;.models&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;MeasurementForUserMixin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;A mixin for getting/creating user-specific measurements.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_queryset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MeasurementForUserMixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="c1"&gt;# ...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MeasurementForUserMixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;form_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;measurement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;\&lt;span class="n"&gt;_success&lt;/span&gt;\&lt;span class="n"&gt;_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;List&lt;/code&gt; inherits from &lt;code&gt;MeasurementForUserMixin&lt;/code&gt; and &lt;code&gt;ListView&lt;/code&gt; &lt;em&gt;in that order&lt;/em&gt;. This means that any method defined in &lt;code&gt;MeasurementForUserMixin&lt;/code&gt; will be found &lt;em&gt;before&lt;/em&gt; the same name is searched for in &lt;code&gt;ListView&lt;/code&gt;. We have selectively overwritten the &lt;code&gt;get_queryset&lt;/code&gt; and &lt;code&gt;create&lt;/code&gt; functions with specific functions. These don't call &lt;code&gt;super&lt;/code&gt; anywhere, so they are definitive.&lt;/p&gt;
&lt;p&gt;Any call in an instance of &lt;code&gt;List&lt;/code&gt; to &lt;code&gt;get_queryset&lt;/code&gt; will now be directed to &lt;code&gt;MeasurementForUserMixin&lt;/code&gt;, &lt;em&gt;even if that code originally comes from &lt;code&gt;ListView&lt;/code&gt;&lt;/em&gt;&lt;label class="margin-toggle sidenote-number" for="sn-example"&gt;&lt;/label&gt;
&lt;input type="checkbox" class="margin-toggle" id="sn-example"&gt;&lt;span class="sidenote"&gt;Yes, I should have added that to the example above, but I noticed too late.&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;This is what we wanted to achieve.&lt;/p&gt;
&lt;p&gt;So, advantages here are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;easily composable, scales well with complexity&lt;/li&gt;
&lt;li&gt;DRY: permission logic is collected in one place and easily swappable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Disadvantages are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;logic is tied to views, not models, which can make finding the functions harder&lt;/li&gt;
&lt;li&gt;can be hard to grasp when there are too many mixins&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Managers and their methods&lt;/h1&gt;
&lt;p&gt;There is one more place where one could put methods pertaining to controlling retrieval of database objects&lt;label class="margin-toggle sidenote-number" for="sn-"&gt;&lt;/label&gt;
&lt;input type="checkbox" class="margin-toggle" id="sn-"&gt;&lt;span class="sidenote"&gt;You could also put that stuff directly into the model, but that wouldn't be &lt;a href="https://en.wikipedia.org/wiki/SOLID"&gt;SOLID&lt;/a&gt; and clutter up the model too much&lt;/span&gt;, and that is the model manager. You know this as &lt;code&gt;.objects&lt;/code&gt;, it's the object responsible for giving you access to the database.&lt;/p&gt;
&lt;p&gt;Let's look at the code, then go through it slowly and understand what's happening.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;MeasurementForUserManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, we define a new &lt;code&gt;Manager&lt;/code&gt; class, simply as a subclass of the regular manager&lt;label class="margin-toggle sidenote-number" for="sn-constructor"&gt;&lt;/label&gt;
&lt;input type="checkbox" class="margin-toggle" id="sn-constructor"&gt;&lt;span class="sidenote"&gt;it is very important that we can not change the constructor for that manager, because that manager gets instantiated with the model class, and manages all database access for that class. This also means we can &lt;em&gt;not&lt;/em&gt; have a manager that "knows" about a specific user or a request. We have to pass in the user/request in the specific functions like &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;create&lt;/code&gt;&lt;/span&gt;
. We overwrite two functions, one will give us a specific queryset and the other will create a correct object &lt;label class="margin-toggle sidenote-number" for="sn-"&gt;&lt;/label&gt;
&lt;input type="checkbox" class="margin-toggle" id="sn-"&gt;&lt;span class="sidenote"&gt;note the use of &lt;code&gt;super&lt;/code&gt; in these methods: these point to the methods of the base class, &lt;code&gt;models.Manager&lt;/code&gt;&lt;/span&gt;. So far, so good. Now we need to hook up this manager in our model class.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;(&lt;span class="n"&gt;models&lt;/span&gt;.&lt;span class="n"&gt;Model&lt;/span&gt;):

    &lt;span class="n"&gt;user&lt;/span&gt; = &lt;span class="n"&gt;models&lt;/span&gt;.&lt;span class="n"&gt;ForeignKey&lt;/span&gt;(&lt;span class="n"&gt;User&lt;/span&gt;, &lt;span class="n"&gt;on_delete&lt;/span&gt;=&lt;span class="n"&gt;models&lt;/span&gt;.&lt;span class="n"&gt;CASCADE&lt;/span&gt;)
    &lt;span class="n"&gt;weight&lt;/span&gt; = &lt;span class="n"&gt;models&lt;/span&gt;.&lt;span class="n"&gt;FloatField&lt;/span&gt;()

    &lt;span class="n"&gt;created&lt;/span&gt; = &lt;span class="n"&gt;models&lt;/span&gt;.&lt;span class="n"&gt;DateTimeField&lt;/span&gt;(&lt;span class="n"&gt;auto_now_add&lt;/span&gt;=&lt;span class="nb"&gt;True&lt;/span&gt;)

    &lt;span class="n"&gt;objects&lt;/span&gt; = &lt;span class="n"&gt;MeasurementForUserManager&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;label class="margin-toggle sidenote-number" for="sn-init"&gt;&lt;/label&gt;
&lt;input type="checkbox" class="margin-toggle" id="sn-init"&gt;&lt;span class="sidenote"&gt;We could also add the user parameter to the constructor &lt;code&gt;__init__&lt;/code&gt; of the class for creation of instances, if we wanted to make sure each &lt;code&gt;Measurement&lt;/code&gt; is attached to the correct user. Since the foreign key is not optional, we'd get error messages for that case anyway.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, using the model in the views looks like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django.http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django.views.generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateView&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;.models&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_queryset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;form_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated_data&lt;/span&gt;
        &lt;span class="n"&gt;measurement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Measurement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;\&lt;span class="n"&gt;_success&lt;/span&gt;\&lt;span class="n"&gt;_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Not that different from doing it manually, right? The difference is that &lt;em&gt;you cannot do it wrong any more&lt;/em&gt;, since the parameter &lt;code&gt;user&lt;/code&gt; is now required, both on &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;create&lt;/code&gt;&lt;label class="margin-toggle sidenote-number" for="sn-all"&gt;&lt;/label&gt;
&lt;input type="checkbox" class="margin-toggle" id="sn-all"&gt;&lt;span class="sidenote"&gt;to be complete, we could also make it a parameter to &lt;code&gt;all&lt;/code&gt;&lt;/span&gt;. Trying to use any of these methods without passing a user will result in an error.&lt;/p&gt;
&lt;p&gt;Also, the actual code that does the filtering is now collected in the manager, close to the actual model. This means that you don't have to remember how the filtering is done each time you use it, and it's close to the model definition. When/if your lookups change, these are easy to find and easy to change in a single place.&lt;/p&gt;
&lt;p&gt;We've essentially made a manager that filters by a foreign key for us, but it could be whatever you need.&lt;/p&gt;
&lt;p&gt;So, advantages here are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DRY: permission logic is in a single place&lt;/li&gt;
&lt;li&gt;model logic lives close to the model&lt;/li&gt;
&lt;li&gt;can be combined with view mixins&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Disadvantages are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you still need to write queryset retrieval for each view&lt;/li&gt;
&lt;li&gt;no "automatic" composition like in mixins&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;We have seen three ways to write permission logic for our views: writing it by hand for each view, writing mixins that compose into the views, and writing model managers that are attached to the model. Each one has their own advantages and disadvantages. In a young project, you'll probably start with writing your views directly. When your project matures, you'll probably start creating some view abstractions into mixins, and write specific logic in model managers. If your project leans more towards view logic, you'll have more mixins. If your project is more database-oriented, you'll have more manager methods.&lt;/p&gt;
&lt;p&gt;So know the tool set, apply it freely and find your own solutions!&lt;/p&gt;</description><pubDate>Sun, 31 Jan 2021 10:00:00 +0100</pubDate></item><item><title>How do you get data into a Django application?</title><link>https://shezi.de//posts/2020-10-30-data-into-django.html</link><description>&lt;p&gt;I was asked "How do you get data into a Django application?".&lt;/p&gt;
&lt;p&gt;Apart from the "regular" manual way through the website, I can see five different options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="#fixtures"&gt;Fixtures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#database"&gt;Write directly into the database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#management"&gt;Through a management command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#api"&gt;Through your API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#external-api"&gt;From an external API&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We'll discuss each of them in the following sections, and there's a &lt;a href="#summary"&gt;summary table at the end&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Fixtures&lt;/h2&gt;
&lt;p&gt;{: #fixtures}&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.djangoproject.com/en/3.0/howto/initial-data/"&gt;Fixtures are django's built-in way to write some data into its database&lt;/a&gt;. It's usually used to get starting data or testing data into a database, but you could easily write some JSON or YAML in some other tool and then load that data into your Django app as a fixture. However, this would normally be a &lt;em&gt;manual&lt;/em&gt; process, not something you'd automate&lt;label class="margin-toggle sidenote-number" for="sn-fixtures"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-fixtures"&gt;&lt;span class="sidenote"&gt;I mean, you could, but it's not super-well made for that&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;As with the direct database access, this circumvents all logic in Django and you need to keep track of all auxiliary data and processing yourself.&lt;/p&gt;
&lt;p&gt;Most often, this technique is used when moving data from one Django installation to another&lt;label class="margin-toggle sidenote-number" for="sn-move-data"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-move-data"&gt;&lt;span class="sidenote"&gt;This is also possible with direct database access, possibly even intra-database transfer. The fixture process is a bit more offline and portable.&lt;/span&gt;. Simply &lt;code&gt;dumpdata&lt;/code&gt; on one end, copy the resulting file, and &lt;code&gt;loaddata&lt;/code&gt; on the other end. This works nicely most of the time, except if you have already used sequences in the database or created your content-types through migrations. Those sometimes clash and you'll have to edit the transfer file.&lt;/p&gt;
&lt;h2&gt;Write directly into the database&lt;/h2&gt;
&lt;p&gt;{: #database }&lt;/p&gt;
&lt;p&gt;Another way to get data into your Django is by interfacing directly with your database. The tables for your models are found easily, they're called &lt;code&gt;appname_modelname&lt;/code&gt;, and their structure can be easily deduced and inspected. You could use direct access with the DB api, e.g. Psycopg or SQLite, or you could use a wrapper like SQLAlchemy.&lt;/p&gt;
&lt;p&gt;This method can be quite fast and versatile, since you're talking directly to a database, with all upsides and downsides that includes. Note, however, that you completely circumvent all Django logic with this; if you have any signals or need to do things with your models, or compute fields or whatever, you'll have to keep track of that in your data importer tool.&lt;/p&gt;
&lt;h2&gt;Through a management command&lt;/h2&gt;
&lt;p&gt;{: #management}&lt;/p&gt;
&lt;p&gt;One of the most convenient and simple ways is to write and run a Django management command. &lt;a href="https://docs.djangoproject.com/en/3.0/howto/custom-management-commands/"&gt;This is easy to do&lt;/a&gt; and will give you access to the Django ORM with all your application settings&lt;label class="margin-toggle sidenote-number" for="sn-management"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-management"&gt;&lt;span class="sidenote"&gt;ie. if you have more than one database, or offline processing or ...&lt;/span&gt;. Now you can get your data in whichever way you want and simply create Django ORM objects with it.&lt;/p&gt;
&lt;p&gt;One neat trick if you have lots of data is to run &lt;a href="https://docs.djangoproject.com/en/3.0/topics/db/optimization/#use-bulk-methods"&gt;bulk inserts&lt;/a&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;YourModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bulk_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;YourModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data_item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;data_item&lt;/span&gt;
        &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data_items&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The great advantage here is that you have full access to Django and all its facilities. The downside is that you &lt;em&gt;have&lt;/em&gt; to run this on the Django server. So this will likely be part of a two-sided tooling: one side that prepares data in whatever way&lt;label class="margin-toggle sidenote-number" for="sn-external-api"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-external-api"&gt;&lt;span class="sidenote"&gt;eg. an &lt;a href="#external-api"&gt;external API&lt;/a&gt; that you query&lt;/span&gt;, the other side that is in the management command and inserts it into the database.&lt;/p&gt;
&lt;p&gt;I would say this is the most common way to do bulk data inserts.&lt;/p&gt;
&lt;h2&gt;Through your API&lt;/h2&gt;
&lt;p&gt;{: #api}&lt;/p&gt;
&lt;p&gt;Naturally, if you have an API, you can easily use that to push data into your system. the main stumbling block you'll encounter here is authentication, and there are several ways around it. You could re-use a session ID by logging in with a browser and inspecting the cookies. Or you could do an actual log-in, but that would entail doing or disabling CSRF, both of which aren't super nice. Or you could use specific API tokens, for example like the ones that Django Rest Framework has optionally available.&lt;/p&gt;
&lt;p&gt;APIs, even internal ones, are a great way to push data from external sources into a Django system, in a distributed and networked way. If you have data sources that produce data at intervals, or if you have different types of sources, or if you simply have lots of sources, an API is usually the best way to get that data into your Django application.&lt;/p&gt;
&lt;h2&gt;From an external API&lt;/h2&gt;
&lt;p&gt;{: #external-api}&lt;/p&gt;
&lt;p&gt;Finally, your Django application can also be an API consumer. This can be done in special views or in a background process. That is, you could have internally accessible views that you trigger manually that execute the API connection, but make sure that they don't time out. Or you put the calls into a background process, with &lt;a href="https://docs.celeryproject.org/en/stable/getting-started/introduction.html"&gt;Celery&lt;/a&gt; or &lt;a href="https://django-background-tasks.readthedocs.io/en/latest/"&gt;Django Background Tasks&lt;/a&gt; or &lt;a href="https://djangopackages.org/grids/g/workers-queues-tasks/"&gt;any other such library&lt;/a&gt;. You can then trigger those calls from views without incurring the delay in the view, or you call them periodically, or from some other source.&lt;/p&gt;
&lt;p&gt;One pattern that comes up often is called "webhooks". They are URLs that get requested from some other system when an event happens&lt;label class="margin-toggle sidenote-number" for="sn-github"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-github"&gt;&lt;span class="sidenote"&gt;One example is with &lt;a href="https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/about-webhooks"&gt;Github&lt;/a&gt;, which can send a POST request whenever an event happens.&lt;/span&gt;. You can then handle that event, which will likely result in calling an external API. Since the trigger should finish quickly, you'll want to do your API access in the background.&lt;/p&gt;
&lt;p&gt;This technique is most useful if your application isn't the leading system or has to aggregate data from different queryable sources.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;{: #summary}&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left;"&gt;Method&lt;/th&gt;
&lt;th style="text-align: left;"&gt;Advantages&lt;/th&gt;
&lt;th style="text-align: left;"&gt;Downsides&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;Fixtures&lt;/td&gt;
&lt;td style="text-align: left;"&gt;easy to do&lt;/td&gt;
&lt;td style="text-align: left;"&gt;quite manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;a bit finicky with sequences and content types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;--------&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;Database&lt;/td&gt;
&lt;td style="text-align: left;"&gt;fast&lt;/td&gt;
&lt;td style="text-align: left;"&gt;manual data processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;super-flexible&lt;/td&gt;
&lt;td style="text-align: left;"&gt;must handle DB changes yourself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;--------&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;Management&lt;/td&gt;
&lt;td style="text-align: left;"&gt;easy to do&lt;/td&gt;
&lt;td style="text-align: left;"&gt;must be on server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;full Django ORM&lt;/td&gt;
&lt;td style="text-align: left;"&gt;usually not frequent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;(ie. usually not automated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;API&lt;/td&gt;
&lt;td style="text-align: left;"&gt;outside access&lt;/td&gt;
&lt;td style="text-align: left;"&gt;API must be maintained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;distributed sources&lt;/td&gt;
&lt;td style="text-align: left;"&gt;requires client logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;simple to do frequent inserts/updates&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;external APIs&lt;/td&gt;
&lt;td style="text-align: left;"&gt;can query many sources&lt;/td&gt;
&lt;td style="text-align: left;"&gt;need to conform to external API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;background process possible&lt;/td&gt;
&lt;td style="text-align: left;"&gt;access must be scheduled&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Choose which suits your situation best. &lt;a href="mailto:j@spielmannsolutions.com"&gt;Or talk to me and I'll discuss it with you.&lt;/a&gt;&lt;/p&gt;</description><pubDate>Fri, 30 Oct 2020 20:00:00 +0100</pubDate></item><item><title>Let's store some energy</title><link>https://shezi.de//posts/2020-08-16-store-some-energy.html</link><description>&lt;p&gt;Let's say, hypothetically, that you are in a place where thermal energy is abundant in the summer, but scarce in winter. I have no idea where the inspiration for this &lt;em&gt;absolutely&lt;/em&gt; hypothetical situation comes from, but bear with me.&lt;/p&gt;
&lt;p&gt;And let's further say that you know that your household uses about 3,000 liters of heating oil every year, which comes out to about 2,520 kg. Since heating oil has a gross heating value&lt;label class="margin-toggle sidenote-number" for="sn-heat-source"&gt;&lt;/label&gt; of about 43 MJ/kg&lt;input type="checkbox" class="margin-toggle" id="sn-heat-source"&gt;&lt;span class="sidenote"&gt;Source: &lt;a href="https://www.engineeringtoolbox.com/fuels-higher-calorific-values-d_169.html"&gt;https://www.engineeringtoolbox.com/fuels-higher-calorific-values-d_169.html&lt;/a&gt;&lt;/span&gt;, that's about 108,360 MJ of heat energy for one year. Quite an awesome number, to be honest.&lt;/p&gt;
&lt;p&gt;As a comparison&lt;label class="margin-toggle sidenote-number" for="sn-standard-german"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-standard-german"&gt;&lt;span class="sidenote"&gt;Source: &lt;a href="https://web.de/magazine/gesundheit/grafik-kalorienaufnahme-weltweit-kalorien-landen-teller-31251562"&gt;https://web.de/magazine/gesundheit/grafik-kalorienaufnahme-weltweit-kalorien-landen-teller-31251562&lt;/a&gt;&lt;/span&gt;, a standard German man uses about 3,300 kcal/day, or 13.8 MJ/day. That comes out to about 5,043 MJ/year. So, you heat about 21 times more energy than you eat&lt;label class="margin-toggle sidenote-number" for="sn-food-oil"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-food-oil"&gt;&lt;span class="sidenote"&gt;In other words, you're consuming about 117 kg of heating oil per year as food. Well, energy-wise.&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, let's say you want to get that heating energy from the sun. Problem is, you need to store it somewhere. Fortunately, one of the best heat-storage substances&lt;label class="margin-toggle sidenote-number" for="sn-spec-heat"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-spec-heat"&gt;&lt;span class="sidenote"&gt;Source: &lt;a href="https://en.wikipedia.org/wiki/Table_of_specific_heat_capacities"&gt;https://en.wikipedia.org/wiki/Table_of_specific_heat_capacities&lt;/a&gt;&lt;/span&gt; that exists is available cheaply and easily almost everywhere: Hydrogen, which has a specific heat of 14.30 kJ/kg·K. Unfortunately, hydrogen is... not easy to store. So you settle for something much more practical&lt;label class="margin-toggle sidenote-number" for="sn-tesla"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-tesla"&gt;&lt;span class="sidenote"&gt;Now I can hear you technologists out there going on about Magical Musk And His Amazing Powerwalls. Well, a new Powerwall 2 stores about 50 MJ of energy for a price of 6,900 € in a volume of 0.13 m³. So you'd need about 2200 powerwalls for a total price of 15,180,000 € and total volume of 280 m³, tightly packed. Not that great!&lt;/span&gt;: water, with a specific heat of 4.18 kJ/kg·K.&lt;/p&gt;
&lt;p&gt;Let's say you get your water at 20°C and store it at close to 100 °C. You'll have it pressurised or something. For every kg of water, that gives you storage for 334 kJ.&lt;/p&gt;
&lt;p&gt;Now it's really simple. You need to store 108,360 MJ, so you need 325,000 kg of water. That's not &lt;em&gt;that much&lt;/em&gt;! Only 325 cubic meters! That &lt;em&gt;easily&lt;/em&gt; fits into a cube of 7 m each side.&lt;/p&gt;
&lt;p&gt;So, the plan is really simple now. Simply dig a basin 7m deep and 7m wide, put a water tank in there, some vacuum plates on the outside and boom, free heating energy! That's only 294 square meters of vacuum insulation, which will cost you about a meager 4-5000 US dollars&lt;label class="margin-toggle sidenote-number" for="sn-vip"&gt;&lt;/label&gt;&lt;input type="checkbox" class="margin-toggle" id="sn-vip"&gt;&lt;span class="sidenote"&gt;Source: &lt;a href="https://www.made-in-china.com/products-search/hot-china-products/Vacuum_Insulated_Panel_Price.html"&gt;https://www.made-in-china.com/products-search/hot-china-products/Vacuum_Insulated_Panel_Price.html&lt;/a&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;figure class="fullwidth"&gt;
          &lt;img src="/assets/not-much-left.png" alt="Screen shot of a price list with one item having -1 quantity available"&gt;
          Unfortunately, our vendor has only -1 of the vacuum plates we need in stock.
        &lt;/figure&gt;

&lt;h2&gt;Ah, but wait&lt;/h2&gt;
&lt;p&gt;you say. Where do you actually get the hot water from?
&lt;label for="mn-but-wait" class="margin-toggle"&gt;&amp;#8853;&lt;/label&gt;
&lt;input type="checkbox" id="mn-but-wait" class="margin-toggle"/&gt;
&lt;span class="marginnote"&gt;
    I mean, apart from the fantastic zero-loss isolation you have around your water tank.
&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Well, solar collectors, obviously. A good, evacuated-panel solar collector can collect about 5 kWh/m²/day on a sunny day&lt;label for="sn-solar-collector" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="sn-solar-collector" class="margin-toggle"/&gt; &lt;span class="sidenote"&gt;Source: &lt;a href="https://en.wikipedia.org/wiki/Solar_thermal_collector"&gt;https://en.wikipedia.org/wiki/Solar_thermal_collector&lt;/a&gt;&lt;/span&gt;, our about 18 MJ/m²/day. You really want to fill that up during the 90 hottest days of the year, so you'll need to collect 1204 MJ/day. Which you can do with only 67 square meters of solar collection. Unfortunately, that's a square of 8.2 m each side, so a bit more than your water tank. But with all the infrastructure you need around it, maybe not.&lt;/p&gt;
&lt;p&gt;Sounds like an easy and cheap plan, right?&lt;/p&gt;</description><pubDate>Sat, 15 Aug 2020 22:40:00 +0200</pubDate></item><item><title>Crime Scene Specialist</title><link>https://shezi.de//posts/2016-07-12-detectivr.html</link><description>&lt;p&gt;Here's an idea for a VR game:&lt;/p&gt;
&lt;p&gt;You are a crime scene specialist, and you get called to crime scenes to find out what happened there. You need to use your expertise, your laboratories and colleagues to accurately reconstruct how the crime was committed.&lt;/p&gt;
&lt;p&gt;In the first mode of the game, you get called to a crime scene. A crime scene is a room that you can move around in and that contains (apart from the crime scene, obviously), lots of stuff. This could be a hotel room, or a living room or a diner or whatever. Inside of that room, you can touch and inspect all the objects and look at everything from every angle. Note that some of the crime scenes might be quite gruesome, so this is definitely a game for adults.
You can apply technology, like fingerprint dust or UV light to find blood, or DNA, or make a GUI in VB to trace the IP of the attacker, or whatever. You can take photos from every angle and with different lights and many more. You can also use your laboratories to send away any samples you've found for more information on them: DNA checks, fingerprint checks, blood checks, material composition checks... and many more. You can also use your colleagues (who can go out into the world) to do background checks, interview witnesses, canvas the neighbourhood, check surveillance tapes and do whatever cops usually do. But you can never leave the room yourself.
However, you talk to yourself, in the form of a voiceover. Whenever you discover something, "your voice" will tell you clues and keep you engaged with the case. You'll also talk to your colleagues in that voice, so that'll be consistent (i.e. the player does an action and the voice comments or carries it out).&lt;/p&gt;
&lt;p&gt;Whenever you have a theory of how the crime could have been done, you can enter the second mode of the game: reconstruction. In this mode, you can set up the scene and all participants, then puppeteer everything to reenact how the crime actually happened. If you can match many of the clues you found in the other mode with your simulation, you'll eventually find out the truth of the case and solve it satisfactorily.&lt;/p&gt;
&lt;p&gt;Your colleagues will then go out and catch the perp. Yay for you!&lt;/p&gt;
&lt;p&gt;SPOILER ALERT: There's a twist in the end.&lt;/p&gt;
&lt;p&gt;The game sort-of follows the advances in crime scene technology. In the first case it'll only be photos and looking at stuff. In the second it'll be fingerprints, in the third it'll be blood samples, in the fourth it'll be DNA samples and so on. The settings will reflect this change, with the first scene set in the 60's, then the 70's, 80's, 90's, 00s and then the 10's.
This also increases the difficulty because you have more different paths you could take.
At the same time, simulation "technology" increases in realism. In the first case, you'll only puppeteer people and will have to roughly line out how it could have happened to solve the crime. In each further case you'll have to be more detailed, e.g. matching scratches and blood spatter successively closer.
In the penultimate case, you'll have full 3D visualisation of all people involved and will have to match every detail perfectly to solve it.&lt;/p&gt;
&lt;p&gt;And then there's the twist: In the last case, all you get to do is look at the crime scene. No laboratories, no colleagues, no equipment. Your reconstruction is prepared with two involved persons, the perpetrator and the victim. You'll have to reenact how the crime happened simply from looking at the scene (but you may look as detailed as you want). The case is a simple one anyway, so you'll be able to easily find a solution to how it happened. However, the simulation is different: you puppeteer the victim, but you have to act out the perpetrator yourself.&lt;/p&gt;
&lt;p&gt;And then you get called to the final case: the inside of a jail cell. You may look at it as long and as detailed as you want, but there's nothing else to do and no simulation either.
The only thing you can do is return to the menu and turn off the game.&lt;/p&gt;
&lt;p&gt;Of course, if you really paid attention, there's foreshadowing: you'll find one of your tools in one case, then probably fingerprints or DNA evidence, all summarily dismissed with some voiceover. "Hmm, must have dropped that here before, better pick it up!" or "Hah, it's me, probably contaminated the scene" or "Damn, I got my prints on it again! Better not tell the boss!". All of which are of course true, since you &lt;em&gt;did&lt;/em&gt; get your DNA in the scenes whenever you did the crime.
Conveniently, all the cases have a solution with a culprit that isn't you.&lt;/p&gt;
&lt;p&gt;And of course, the whole schema isn't new: an investigator that is also the offender. There are probably other games that do a simulation of it. Certainly movies. However, we're using VR to place the player into the shoes of the actual actor, and the game is designed around slowly leading the player into doing the crimes themselves. The simulation aspect will get more realistic during the game until it culminates in actually, quite realistically committing the last crime without any outside help. At that point, the culprit isn't identified in the simulation, but instead the player has to act in their stead. In fact, they are committing the crime in that moment and they will be made to feel it that way right after it. The emotional impact of that setting should be much higher than other media can deliver.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;small&gt;
If you want to make this game, go ahead. A writing credit would be appreciated.&lt;br /&gt;
If you want to make this game &lt;em&gt;with me&lt;/em&gt;, let me know and we'll do it!
&lt;/small&gt;&lt;/p&gt;</description><pubDate>Tue, 12 Jul 2016 21:00:00 +0200</pubDate></item><item><title>What is mathematics?</title><link>https://shezi.de//posts/2016-07-11-what-is-mathematics.html</link><description>&lt;p&gt;I was recently asked by a friend what mathematics actually is. And what mathematicians actually do all day long.&lt;br /&gt;
In this post I will try to answer these questions.&lt;/p&gt;
&lt;p&gt;But first, let me rant a little.&lt;/p&gt;
&lt;p&gt;Imagine you want to learn how to do art. You're fortunate because the regular school curriculum has a subject called "Art". Once you enter it, all you'll ever do is transcribe scores. You get books upon books of scores and are supposed to write down the individual notes as letters. Nothing else, ever. No painting, no reading, no looking at paintings, no listening to music, and certainly no playing music. Only transcribing, for twelve years or more.&lt;br /&gt;
Do you think you'd be interested in Art after that treatment? No? Yet that's exactly what happens with "Mathematics" in school. Instead of showing the wonderful beauty that is mathematics, students are supposed to &lt;em&gt;calculate&lt;/em&gt;, of all things. I'm sure that in the early 1900's, when computers weren't available and people had to to accounting and bookkeeping and calculating all by themselves, this was probably a great idea. After all, the "modern" school system is mainly set up to prepare young people to enter the industry as usable workers.&lt;/p&gt;
&lt;p&gt;In any case, that's not mathematics. That's computation and since the 1960s probably best left to, you know, computers&lt;label class="margin-toggle sidenote-number" for="sn-computers"&gt;&lt;/label&gt;.&lt;input type="checkbox" class="margin-toggle" id="sn-computers"&gt;&lt;span class="sidenote"&gt;In fact, before we had computing machines, the word "computer" referred to a &lt;a href="https://en.wikipedia.org/wiki/Computer#Etymology"&gt;person doing computations&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Mathematics is something completely different from this drudgery and torture.&lt;br /&gt;
Mathematics is a game.&lt;/p&gt;
&lt;p&gt;The game is very simple: You start out with a handful of simple statements about something. The goal of the game is to find out as much as you can about the things the statements are about.&lt;br /&gt;
That, more or less, is all there is. There are a few rules what you can do with the statements and the things they are about and the way of finding stuff out, but they're not complicated, so you could learn them in a few hours.&lt;/p&gt;
&lt;p&gt;The amazing thing, however, is that starting from very few little facts, you can find out a vast number of things. You'll find out lots of interesting facts about the things you're thinking about, and you'll get much better at the game. How much you find out that's your score. If you find out more things your score is higher. Of course, your score is given out by fellow mathematicians so the better you can describe what you've found out concisely and clearly and short the higher your score will be.
Some things you will &lt;em&gt;feel&lt;/em&gt; are right, but they're not and you'll prove it&lt;label class="margin-toggle sidenote-number" for="sn-now"&gt;&lt;/label&gt;.&lt;input type="checkbox" class="margin-toggle" id="sn-now"&gt;&lt;span class="sidenote"&gt;Naturally, &lt;em&gt;disproving&lt;/em&gt; is also &lt;em&gt;finding something out&lt;/em&gt;, so you'll increase your score.&lt;/span&gt; Some things you will feel are right but no one can prove it -- that's when mathematicians get edgy and start &lt;a href="https://en.wikipedia.org/wiki/Hilbert%27s_problems"&gt;challenging the world&lt;/a&gt; to prove them or even hand out &lt;a href="https://en.wikipedia.org/wiki/Millennium_Prize_Problems"&gt;million-dollar prizes&lt;/a&gt; to anyone who can prove it.&lt;/p&gt;
&lt;p&gt;After a while of this, however, you will come to a wall. You will have exhausted everything there is to find out about the things you started with. Now starts the second part of the game. The second part is even better, because now you get to define new parts of the game. There's nothing more to find out, so you just think of something new, and then start finding out about these new things. The less you need to say about the new things and the more you and others can find out about them, the higher your score. Some old mathematicians had such high scores that we attached their &lt;a href="https://en.wikipedia.org/wiki/List_of_things_named_after_Henri_Poincar%C3%A9"&gt;names&lt;/a&gt; to the &lt;a href="https://en.wikipedia.org/wiki/List_of_things_named_after_Srinivasa_Ramanujan"&gt;things&lt;/a&gt; they &lt;a href="https://en.wikipedia.org/wiki/List_of_things_named_after_Paul_Erd%C5%91s"&gt;defined&lt;/a&gt;. Not many people play on this level, so all of those are pretty &lt;a href="https://en.wikipedia.org/wiki/List_of_things_named_after_Isaac_Newton"&gt;well known&lt;/a&gt;&lt;label class="margin-toggle sidenote-number" for="sn-wellknown"&gt;&lt;/label&gt;.&lt;input type="checkbox" class="margin-toggle" id="sn-wellknown"&gt;&lt;span class="sidenote"&gt;In fact, for the top player, &lt;a href="https://www.britannica.com/biography/Leonhard-Euler"&gt;Leonhard Euler&lt;/a&gt;, we started &lt;a href="https://en.wikipedia.org/wiki/List_of_things_named_after_Leonhard_Euler"&gt;naming things after the first person to find out things &lt;em&gt;after&lt;/em&gt; him&lt;/a&gt;, just because his list would be so large. Even so, it is a very long list.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But, as you'll probably think by now, that gets old after a while, too. So then starts the third part of the game.
Because now that you have a decent overview over mathematics and the things it's about, you'll start to see the structure behind it. Wildly different fields of mathematics will start so seem similar and you'll start to see the universe behind it. You'll start to see that, no matter where you start, people will think along the same lines and things will be more similar than they seem at first. And if you can then connect two seemingly different things, that's when the &lt;em&gt;really&lt;/em&gt; high scores come out. Almost no one plays on this level, so everyone here is revered as a hero of mathematics. You might even have heard some of those names: Leonhard Euler, Isaac Newton, Evariste Galois, Erdős Pál, Andrew Wiles, ... Or, probably you haven't&lt;label class="margin-toggle sidenote-number" for="sn-ties"&gt;&lt;/label&gt;.&lt;input type="checkbox" class="margin-toggle" id="sn-ties"&gt;&lt;span class="sidenote"&gt;Each of theses is worth looking up, by the way.&lt;/span&gt; &lt;/p&gt;
&lt;p&gt;At that point it isn't even about the game any more. It's about much more, as you've glimpsed the structure of the human mind and of the world, because you've seen the ties that hold together all those different mathematical descriptions of the world. You've seen that mathematics is the canvas on which the universe is painted.&lt;br /&gt;
And it's beautiful, it's glorious. Everything is connected, no matter where you start.&lt;/p&gt;
&lt;p&gt;That is mathematics.&lt;/p&gt;
&lt;p&gt;You probably think you know what mathematics is, because you had that subject called "Mathematics" in school. Well, you don't, because what's taught there is just a tiny subset of mathematics called arithmetic, which today is usually better left to machines. The reason you had to learn this is because when the modern schooling system was created in the early 1900's, those machines didn't exist yet, so people had to learn to do the computations themselves in order to be accountants and bookkeepers. But, of course, other people have pointed out &lt;a href="http://www.ted.com/talks/ken_robinson_says_schools_kill_creativity?language=en"&gt;this century-old flaw in our education a lot more eloquently than I can&lt;/a&gt;.
In any case, people learn computing and think they know what mathematics is.&lt;/p&gt;
&lt;p&gt;Most of those people will miss out on the beauty that is mathematics.&lt;/p&gt;
&lt;p&gt;And that is very sad.&lt;/p&gt;</description><pubDate>Mon, 11 Jul 2016 00:20:00 +0200</pubDate></item><item><title>Enterprise sales</title><link>https://shezi.de//posts/2016-07-10-enterprise-sales.html</link><description>&lt;p&gt;I'm currently trying out my hand on enterprise sales. Meaning I'm trying to sell my services to (at least) one large company.&lt;/p&gt;
&lt;p&gt;The situation is this: There is an enterprise that has a need. The need is very clearly defined, meaning that they know exactly what it is that they need and how the solution is supposed to look. They do not know the technical details, but they don't need to, otherwise they wouldn't need an external supplier. The need is also unserved at the moment, and has been so for at least 15 years. They have a budget for their need, and since this is an important business and legal need, they spend that budget on fulfilling the need manually, ie. with paper and personnel. Essentially, they're stuck in the 20th century and want to upgrade to an internet-based service, both for modernity, for customer reach and for cost reasons. For the past 15 years, they didn't find a provider or a solution that could elevate their need into the internet.&lt;/p&gt;
&lt;p&gt;I have a prototype for their need. This prototype clearly demonstrates how I would solve their business need and the necessary maths behind it. It's not simple, but straightforward.&lt;/p&gt;
&lt;p&gt;I would like to present that prototype to the enterprise, showing them how easy it would be for them to get their need fulfilled and how reasonably priced.&lt;/p&gt;
&lt;p&gt;But they don't want to see it. They say they're "not ready for this yet". Wow!&lt;/p&gt;
&lt;p&gt;Ok, so I called their next-largest competitor which has the exact same need. Legal necessities and so on.&lt;br /&gt;
Or rather, I tried calling them, because I can't even reach anyone there.&lt;/p&gt;
&lt;p&gt;Enterprise sales are &lt;em&gt;frustrating&lt;/em&gt;.&lt;/p&gt;</description><pubDate>Sun, 10 Jul 2016 22:00:00 +0200</pubDate></item><item><title>The Bank Argument</title><link>https://shezi.de//posts/2016-05-13-bank.html</link><description>&lt;p&gt;All of a sudden, two large companies have become champions for privacy. That is suprising, and we have to ask ourselves: Why?&lt;/p&gt;
&lt;p&gt;The first one is of course Apple, which very publicly defended our right to privacy on our phones even if the person in question is a known and dead ter'rist.
They vehemently fought against the FBI who wanted them to create a software update that can unlock an iphone&lt;label class="margin-toggle sidenote-number" for="sn-order"&gt;&lt;/label&gt;.&lt;input type="checkbox" class="margin-toggle" id="sn-order"&gt;
&lt;span class="sidenote"&gt;Strangely enough, they were compelled to unlock the phone using the "All Writs Act" from &lt;strong&gt;1789&lt;/strong&gt;.&lt;/span&gt; That is, the FBI wanted a backdoored iOS and wanted it installed on a specific phone&lt;label class="margin-toggle sidenote-number" for="sn-now"&gt;&lt;/label&gt;.&lt;input type="checkbox" class="margin-toggle" id="sn-now"&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="sidenote"&gt;Of course, it can be assumed that this would only be the first phone that the FBI wanted to unlock, which is one reason that Apple fought against the order.&lt;/span&gt;
The case was dropped only when an outside party offered to unlock the phone for the FBI, so the agency didn't really need the help for Apple in this case any more.&lt;/p&gt;
&lt;p&gt;Apple didn't use to be so vehement about their users' privacy, so why this, and why now? Some people think this is about &lt;em&gt;the Bank Argument&lt;/em&gt;, and it's interesting enough to look at. But before that, there's &lt;em&gt;another&lt;/em&gt; recent example of a company putting user privacy interests forward.&lt;/p&gt;
&lt;p&gt;And that company is Facebook. In the beginning of April 2016, Facebook introduced full end-to-end encryption for all users of its WhatsApp messaging service. And it's not even homegrown somewhat-security like &lt;a href="http://security.stackexchange.com/a/49802"&gt;some other messaging apps use&lt;/a&gt;. This is TextSecure from WhisperSystems, one of the most respected and well-known vendors in that area. This turns WhatsApp into one of the strongest and most widely-reaching encryption deployments of the world. The &lt;a href="https://www.eff.org/node/82654"&gt;EFF has upgraded their rating of WhatsApp&lt;/a&gt;, and it is now as high as Swiss-made Threema, the only thing keeping it from a top rating being its closed-source-ness. But the question remains: Why would they do that? It costs money, it costs them opportunities&lt;label class="margin-toggle sidenote-number" for="sn-ads"&gt;&lt;/label&gt;
&lt;input type="checkbox" class="margin-toggle" id="sn-ads"&gt;
&lt;span class="sidenote"&gt;For example, if you have access to chat messages, you can mine them for &lt;a href="http://www.sentiment140.com/"&gt;sentiment analysis&lt;/a&gt;, or &lt;a href="https://support.google.com/mail/answer/6603?hl=en"&gt;ads&lt;/a&gt;, or even just for &lt;a href="http://www.zdnet.com/article/microsoft-launches-ai-chat-bot-tay-ai/"&gt;training your next super-AI&lt;/a&gt;.&lt;/span&gt;, it &lt;a href="http://www.bbc.com/news/world-latin-america-35119235"&gt;costs them marketshare&lt;/a&gt; and it's not really something that they need to stay on top of the game, being the most widely used messenger anyway.&lt;/p&gt;
&lt;p&gt;So, why?&lt;/p&gt;
&lt;p&gt;One argument that seems clever and compelling at first is the Bank Argument. Apple and Facebook do this because they want to become &lt;em&gt;banks&lt;/em&gt;. To do this, you need the trust of your users and secure ways of contacting them, and secure ways for your users to connect with other users. So people have speculated that both &lt;a href="http://www.antipope.org/charlie/blog-static/2016/03/follow-the-money-apple-vs-the-.html"&gt;Apple&lt;/a&gt; and &lt;a href="https://www.thinkbusiness.ie/articles/facebook-bank/"&gt;Face&lt;/a&gt;&lt;a href="http://digiday.com/platforms/social-growth-stalling-facebook-still-wants-bank/"&gt;book&lt;/a&gt; want to become banks&lt;label class="margin-toggle sidenote-number" for="sn-apple-ceo"&gt;&lt;/label&gt;
&lt;input type="checkbox" class="margin-toggle" id="sn-apple-ceo"&gt;
&lt;span class="sidenote"&gt;Apple's CEO even very suspiciously &lt;a href="http://www.ibtimes.com/apple-working-iphone-iphone-payments-tim-cook-says-it-does-not-want-become-bank-2180908"&gt;stated&lt;/a&gt; that they &lt;em&gt;do not want to become a bank&lt;/em&gt;. If that's not a giveaway I don't know what is!&lt;/span&gt;. The arguments for this are varied. &lt;a href="http://digiday.com/platforms/social-growth-stalling-facebook-still-wants-bank/"&gt;They want to become a bank because they need more money&lt;/a&gt;. &lt;a href="https://www.thinkbusiness.ie/articles/facebook-bank/"&gt;They want to become a bank to know more about you&lt;/a&gt;. &lt;a href="http://9to5mac.com/2015/06/22/opinion-apple-bank/"&gt;They want to become a bank because other banks are shit&lt;/a&gt;. &lt;a href="http://www.antipope.org/charlie/blog-static/2016/03/follow-the-money-apple-vs-the-.html"&gt;They want to become a bank because they have too much money&lt;/a&gt;; that last one being particularly amusing.&lt;/p&gt;
&lt;p&gt;To be honest: I think it would be a great move for any of the big tech companies to become banks. Simply because banks are shit. We all know this from the last time they crashed our world economy and got paid billions for that. But also because they're slow, expensive and not very customer-oriented; and I think that BigTech can do those things better than BigFinance.&lt;br /&gt;
It could also be very profitable for those companies to turn into banks, as online payments already generate billions in revenues for some &lt;a href="http://www.nasdaq.com/symbol/v/revenue-eps"&gt;shady&lt;/a&gt; &lt;a href="http://www.nasdaq.com/symbol/ma/revenue-eps"&gt;companies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But I also think that they &lt;em&gt;won't&lt;/em&gt; become banks. One indication for that is that they're not already banks. Apple, Facebook and Google already have payment systems. Facebook even had their own currency, the &lt;a href="https://en.wikipedia.org/wiki/Facebook_Credits"&gt;Facebook Credit&lt;/a&gt;, which could trivially be turned into a bank account, but was cancelled after a few years. There's no good reason to turn these payment systems into "real" banks, but it would come with hugely increased regulation and oversight. Being a bank is not as easy as many commenters make it sound, so without a really great incentive, becoming a bank isn't such a great proposition. That's probably why Facebook spun out their credits system into a separate company, Facebook Payments Inc.&lt;br /&gt;
They might become more &lt;em&gt;like&lt;/em&gt; banks, offering services that banks used to offer, but they will probably not become full-blown banks. There are just too many downsides to that.&lt;/p&gt;
&lt;p&gt;As &lt;a href="http://social.yourstory.com/2015/01/quotes-bill-gates-mobile-banking/"&gt;Bill Gates said&lt;/a&gt;, Banking is neccessary, banks are not.&lt;/p&gt;</description><pubDate>Fri, 13 May 2016 12:00:00 +0200</pubDate></item><item><title>We were promised superpowers!</title><link>https://shezi.de//posts/2016-01-08-superpowers.html</link><description>&lt;p&gt;But instead, we get computers we can't open. We get fancy CSS without any structure. We get Javascript that we can't trust, and spam in our inboxes. We get ads and celebrities and diversions and "free-to-play", and everything but actual data.&lt;/p&gt;
&lt;p&gt;You don't believe me?&lt;/p&gt;
&lt;p&gt;Try any of these things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find the price of a Nintendo game when it was released and inflation-adjust that price. Compare with current video game prices.&lt;/li&gt;
&lt;li&gt;Which of the video games in your library has the best time/money ratio? Ie. for which game did you pay the least per hour played. Now inflation-adjust it!&lt;/li&gt;
&lt;li&gt;If you save one € every day for the next 40 years into a compounding interest account, how much will you have at the end? Now inflation-adjust it!&lt;/li&gt;
&lt;li&gt;Find the cheapest flight from here to there with yourself and your whole family. Find the cheapest final price, including all of the bogus taxes.&lt;br /&gt;
 Now be flexible about the date.&lt;/li&gt;
&lt;li&gt;Find that one picture from that castle that you saw on that vacation. Do it without giving all your pictures to an american company.&lt;/li&gt;
&lt;li&gt;Find all pictures of your significant other. Again, without giving them to any company. Include profile and half-hidden pictures.&lt;/li&gt;
&lt;li&gt;How many times did you go to the cinema last year? And how often with your best friend?&lt;/li&gt;
&lt;li&gt;Draw five random cards. Now tell us the probability of winning against your three mates in five-card-draw poker. Also, how many and which cards should you exchange for maximum winning probability?&lt;/li&gt;
&lt;li&gt;You're on a train, and the train is delayed. Are there any alternative routes you can take that get you there quicker?&lt;/li&gt;
&lt;li&gt;Find the furthest place that you can get to for 20 €, with any means of transport.&lt;/li&gt;
&lt;li&gt;Find the cheapest way to travel 500 km in any direction.&lt;/li&gt;
&lt;li&gt;Find the fastest way to travel 500 km in any direction.&lt;/li&gt;
&lt;li&gt;You're in a car, driving to your parents' place. You get hungry. Find a place where you can eat with minimum detour within the next 30 km. Same for fuel. Combine the two.&lt;/li&gt;
&lt;li&gt;You took a picture with your cell phone. Now look at it on your computer. Do it without giving all your data to a company.&lt;/li&gt;
&lt;li&gt;When will your driver's license expire? Will someone remind you?&lt;/li&gt;
&lt;li&gt;Given the contents of your fridge, what is a meal you could cook with it? What's an ingredient you could buy to make more meals?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We were promised superpowers. Instead, we get the copyright police, corporate interests, walled gardens, corrupt politics and constant surveillance.&lt;/p&gt;</description><pubDate>Fri, 08 Jan 2016 21:40:00 +0100</pubDate></item><item><title>Welcome to Jekyll!</title><link>https://shezi.de//posts/2016-01-08-welcome-to-jekyll.html</link><description>&lt;p&gt;I've just updated my blog to use &lt;a href="http://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;. And with smooth &lt;a href="https://edwardtufte.github.io/tufte-css/"&gt;Tufte CSS&lt;/a&gt; as well. Everything is awesome&lt;label class="margin-toggle sidenote-number" for="sn-now"&gt;&lt;/label&gt;.
&lt;input type="checkbox" class="margin-toggle" id="sn-now"&gt;
&lt;span class="sidenote"&gt;now&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I intend to write more stuff, so stay tuned.&lt;/p&gt;</description><pubDate>Fri, 08 Jan 2016 20:00:00 +0100</pubDate></item><item><title>Python basics: Functions and arguments</title><link>https://shezi.de//posts/2014-07-18-python-basics-function-syntax.html</link><description>&lt;p&gt;I've recently talked to some guys and girls about functions in Python and how to define and call them. Here's the distillation of everything that's interesting about the topic&lt;label for="mn-demo" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;&lt;input type="checkbox" id="mn-demo" class="margin-toggle"&gt;&lt;span class="sidenote"&gt;By the way, I won't put comments in all of my functions, because they are so very short and just for illustration. You should &lt;em&gt;&lt;em&gt;always&lt;/em&gt;&lt;/em&gt; put documentation in &lt;em&gt;your&lt;/em&gt; functions, though!&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The first thing we do is define a function without any parameters. There syntax for this is:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;this is fun&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This defines a function &lt;code&gt;fun&lt;/code&gt; that can be called and that prints out the string given in its &lt;em&gt;function body&lt;/em&gt;. What's the "function body", you ask? It's all the indented lines following &lt;code&gt;def fun():&lt;/code&gt; (which, coincidentally, is called &lt;em&gt;function head&lt;/em&gt;), and it's those lines that are executed when you call the function. Let's try it out:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, you call functions by giving them a list of arguments in parentheses -- and in our case, that list is empty. If you omit the argument list (sometimes also called parameter list), something entirely different happens:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x10796b440&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What does &lt;em&gt;that&lt;/em&gt; mean? It means that instead of &lt;em&gt;calling&lt;/em&gt; the function, you &lt;em&gt;pointed&lt;/em&gt; at the function. Functions in Python behave like any other value, so you could assign them to a variable or pass them as a parameter to another function. But more about that later.&lt;/p&gt;
&lt;p&gt;Our function doesn't &lt;em&gt;return&lt;/em&gt; anything, so calling it doesn't produce a value. Well, actually, "no value" in Python means &lt;code&gt;None&lt;/code&gt;, and we can see that, too:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One of the most important use cases of functions, however, is to compute something, which means we have to be able to indicate what the value of a function call should be. That's what the keyword &lt;code&gt;return&lt;/code&gt; does. It works like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;this is still fun&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;enjoyment&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And we get the value back when we call the function:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;enjoyment&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So let's say, we want to define a function that adds two variables. To do that, we need to tell Python that our function should accept two parameters:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We already know what &lt;code&gt;return&lt;/code&gt; means, but now the &lt;em&gt;argument list&lt;/em&gt; isn't empty any more. Instead, it has two entries, &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;, and that means you also have to call this function with two arguments:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can &lt;em&gt;not&lt;/em&gt; call the function with any other number of parameters, or you'll get an error:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="n"&gt;positional&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;takes&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;positional&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="n"&gt;but&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;were&lt;/span&gt; &lt;span class="n"&gt;given&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The arguments we defined here are so-called &lt;code&gt;positional&lt;/code&gt; arguments. They are called this because their position, or order, is important. Calling a function with a different order of arguments changes the meaning, like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Positional arguments, because they are identified by their position.&lt;/p&gt;
&lt;p&gt;But what if we want to allow someone to only specify one argument, assuming that people always mean 0 when they don't say anything? In that case, we can turn one of our positional arguments into keyword arguments, or default arguments&lt;label for="sn-demo" class="margin-toggle sidenote-number"&gt;&lt;/label&gt;.
&lt;input type="checkbox" id="sn-demo" class="margin-toggle"/&gt;
&lt;span class="sidenote"&gt;These two words actually have distinct, specific meanings in Python 3. However, this difference is not very important in most use cases, so I'll use them interchangeably.&lt;/span&gt;
 This means that we specify a value that should be used when the argument is absent in the call:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I can now leave the value for &lt;code&gt;b&lt;/code&gt; out of the call and Python will substitute a zero for it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This argument is now a &lt;em&gt;default&lt;/em&gt; argument, because it can be defined either by its position or by its default value. It can &lt;em&gt;also&lt;/em&gt; be identified by is name, and that's why it's not a positional argument any more (as you saw above, it's still positional, too, though). Have a look at this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Both &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are now default arguments that I can omit, or I can identify them by their name -- and then the order isn't important any more. Here's how you can call this new &lt;code&gt;sub&lt;/code&gt; function:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you saw here, we can call this function like we did before, with positional arguments. We can also skip any of the arguments and get a value of 0 for those. We can also identify the arguments by their name, regardless of the order. That's also why they're called keyword arguments -- instead of their position I can call them by their keyword, their name.&lt;/p&gt;
&lt;p&gt;For most parts, this can get you very far. You can now create functions with as few or as many arguments as you need, and you can make some or all of these arguments with default values, so we can skip them or call them by their name.&lt;/p&gt;
&lt;h2&gt;The hitch with keyword arguments&lt;/h2&gt;
&lt;p&gt;Of course, not all is easy in the land of computers, so there's a hitch that we have to look out for when using keyword arguments. These arguments are only initialized when the function is defined, so they become what other languages would call &lt;em&gt;static&lt;/em&gt;, they don't change from call to call.&lt;/p&gt;
&lt;p&gt;Well, what do I mean by that now?! It means that when you give a keyword argument a default value that can change, like a list, a dictionary or an object, you'll be surprised. Let's have a look:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;wrong_insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Insert a into the given list.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now when you use &lt;code&gt;insert&lt;/code&gt;, at first nothing surprising happens:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wrong_insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wrong_insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wrong_insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# we can omit the keyword argument here!&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, now something completely strange appears:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wrong_insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# again!&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Wait, what now?! Why is &lt;code&gt;1&lt;/code&gt; twice in that list? Because you inserted twice into the same list. That default value is only created once when you define the function, so when you append something to the list, you append it &lt;em&gt;to the default value of that parameter&lt;/em&gt;. So how do we do it right? We assign a default value that cannot be changed and test for it. There's one value that's perfectly suited for that task, and it's &lt;code&gt;None&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;right_insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ls&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This one works as expected:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;right_insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;right_insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;right_insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;right_insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Be very, very careful when you see a list, a dictionary or an object as a default value. Changing that value will happen and you probably won't expect it.&lt;/p&gt;
&lt;p&gt;There's more or less only one use case for putting a mutable value (like a dictionary) into a default value, and that's when you use it as a cache. You should certainly call it &lt;code&gt;CACHE&lt;/code&gt; in that case, too! Here's an example:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CACHE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{}):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;CACHE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CACHE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;CACHE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That's a neatly encapsulated cache. Since we named it in ALL-CAPS, we know that it's sort-of global, so we'll be on the lookout when finding such a variable.&lt;/p&gt;
&lt;p&gt;If you see any other list, dictionary or object as a default parameter value, be suspicious!&lt;/p&gt;
&lt;h2&gt;varargs, lots of positional arguments&lt;/h2&gt;
&lt;p&gt;But what if you don't know the number of positional arguments beforehand? Say, you want to add two &lt;em&gt;or more&lt;/em&gt; numbers? There's a way to do that and it's called &lt;code&gt;varargs&lt;/code&gt;, from "variable number of arguments". The syntax for that in Python is:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And let's try it out:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;54&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So what happens here is that any argument that is not positional gets stuffed into a list, and this list is called &lt;code&gt;args&lt;/code&gt;, because we gave it that name with an asterisk in front of it in the function definition. Any other name works as well, but it's a pretty strong convention to use &lt;code&gt;args&lt;/code&gt; here. And as you see, we can have any number of arguments, even none at all. Of course we can still specify that we want positional arguments in front of it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can &lt;em&gt;not&lt;/em&gt; omit the first argument. It's a positional argument, so you cannot omit it. But we can omit the varargs:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="n"&gt;positional&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;start&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Can you have more than one varargs?&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;moreargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;moreargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                      &lt;span class="o"&gt;^&lt;/span&gt;
&lt;span class="ne"&gt;SyntaxError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="n"&gt;syntax&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No, you can't. And what would be the meaning of that anyway? Which list should Python add the arguments to? No, no, that doesn't mean anything, so you it's a syntax error, as it should be.&lt;/p&gt;
&lt;p&gt;Did you notice what simple trick Python does here for us? It takes the arguments that you pass into a function that don't go anywhere else, stuffs them into a list and gives us that list. It's really all that happens.&lt;/p&gt;
&lt;p&gt;But interestingly enough, the trick works in reverse, too: When you have a list of values that you want to use as arguments to a function, you can tell python to do just that, and with the &lt;em&gt;same&lt;/em&gt; syntax, too:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arglist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arglist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arglist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arglist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;71&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Don't forget that asterisk, though:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arglist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subber&lt;/span&gt;
&lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unsupported&lt;/span&gt; &lt;span class="n"&gt;operand&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;int&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;list&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;kwargs, lots of keyword arguments and lots of other arguments&lt;/h2&gt;
&lt;p&gt;So let's say you want to define a function that has a keyword argument, but you don't know what keyword that'll be, and you also don't know how many it should be. Sounds unlikely, but it's actually very useful when you do dynamic processing or datastructures you don't know much about.&lt;/p&gt;
&lt;p&gt;Of course, there's a syntax for it as well, and it's pretty similar to the varargs we saw before. It's called kwargs, for keyword arguments, uses two asterisks and instead of a list of arguments you get a dictionary:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And it works as expected:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;world&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;goodbye&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lonelyness&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;world&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;goodbye&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;lonelyness&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Just as we saw before, the same works in reverse: When I have a dictionary with values that I want to use to call a function, I can just tell Python to unpack them into arguments:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;doing stuff with key &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt; and lock &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;and more arguments: &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;lock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Locke&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;something&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;else&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;doing&lt;/span&gt; &lt;span class="n"&gt;stuff&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="n"&gt;Locke&lt;/span&gt;
&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;something&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;else&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So now we have all parts together and we can actually stick them together, so here's the full syntax for function definitions at (least in Python 2.7):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Try it out for yourself!&lt;/p&gt;
&lt;p&gt;Actually, there's one more thing: You &lt;em&gt;could&lt;/em&gt; add in keyword arguments in the middle, between &lt;code&gt;b&lt;/code&gt; and &lt;code&gt;*args&lt;/code&gt;, but it wouldn't work the way you expected it to, and &lt;em&gt;that&lt;/em&gt; would be confusing and unintuitive. So don't do that!&lt;/p&gt;
&lt;h2&gt;Functions are values!&lt;/h2&gt;
&lt;p&gt;So why is the &lt;code&gt;args&lt;/code&gt; and &lt;code&gt;kwargs&lt;/code&gt; dance so useful? Especially &lt;code&gt;kwargs&lt;/code&gt; -- what good could it be when you don't know the parameters to your function?&lt;/p&gt;
&lt;p&gt;To make this work for us, we have to know one more great thing about functions: they are values, just like everything else is. So you can rename functions, store them in variables, put them in lists and even pass them to other functions. Using &lt;code&gt;fun&lt;/code&gt; from above, let's see:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;morefun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;morefun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;funlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;morefun&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;funlist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;funfun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;calling f&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;funfun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;calling&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That last bit is really interesting: we can pass functions into functions. We can also return functions from functions -- because they're simply values that behave like any other.&lt;/p&gt;
&lt;p&gt;And finally, we can also &lt;em&gt;define&lt;/em&gt; functions inside of functions. In a way, when you write &lt;code&gt;def f(...):...&lt;/code&gt;, this is just shorthand for &lt;code&gt;f = &amp;lt;some function&amp;gt;&lt;/code&gt;, and it will behave exactly like any other variable. Think of functions as any other variable, just one that you can call to get a new value.&lt;/p&gt;
&lt;p&gt;So now let's put all this together. See if you can figure out what this next example does:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;log_fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;inside&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;called with &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt; and &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;resulted in &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Wow! We defined a function that takes a function, creates a function, uses a function inside the inner function, and then returns that inner function. So, what does it do? &lt;code&gt;log_fun&lt;/code&gt; &lt;em&gt;wraps&lt;/em&gt; the function we passed as parameter &lt;code&gt;f&lt;/code&gt;, and returns a function that does the wrapping. The wrapping function then prints the arguments and kwarguments, calls &lt;code&gt;f&lt;/code&gt; with them, prints the result of the call and finally returns the result of the call. All without knowing anythig about the parameters of &lt;code&gt;f&lt;/code&gt; itself. From a parameter/return perspective, it behaves quite like &lt;code&gt;f&lt;/code&gt; does -- only it prints out the parameters and results in between. How do we use it? Let's try:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;new_f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;new_f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;called&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;resulted&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You could be even more daring and &lt;em&gt;replace&lt;/em&gt; &lt;code&gt;fun&lt;/code&gt; with that new logged version of fun:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;called&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;resulted&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Pretty neat, eh? You simply wrapped your function in some logging, without caring what the actual parameters are. That &lt;code&gt;log_fun&lt;/code&gt; function is pretty useful when you want to debug your function calls without changing the functions themselves.&lt;/p&gt;
&lt;p&gt;Since wrapping, enhancing or &lt;em&gt;decorating&lt;/em&gt; functions is such a useful concept, there's a special syntax for it in python, with an &lt;code&gt;@&lt;/code&gt;. These next two examples are exactly the same:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;X!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;something&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;something&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and this one:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@log_fun&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;X!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And now you know what a &lt;em&gt;Decorator&lt;/em&gt; is in python: It's a function &lt;code&gt;d&lt;/code&gt;that takes a function &lt;code&gt;f&lt;/code&gt; and returns a function &lt;code&gt;f'&lt;/code&gt;, and in-between does something with the original function &lt;code&gt;f&lt;/code&gt; -- effectively enhancing the things &lt;code&gt;f&lt;/code&gt; does. Don't be alarmed by the syntax, the two blocks above are exactly identical, semantically, so if you ever see one of the two, you can replace it with the other without fear.&lt;/p&gt;
&lt;p&gt;There's more to say about decorators, but there's another document for that.&lt;/p&gt;
&lt;h2&gt;Overloading&lt;/h2&gt;
&lt;p&gt;If you ever programmed in a compiled language like C, C++ or Java, you might have heard the term "overloading a function". It's a cool concept, and it basically means that two functions or methods can have the same name but a different parameter list. Like this (in Java):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OverloadingDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;simpleFunc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;simpleFunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;called&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;simpleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;simpleFunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;called&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;simpleFunc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;simpleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you compiled that program and ran it, the output would be:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;simpleFunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;called&lt;/span&gt;
&lt;span class="nx"&gt;simpleFunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;called&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The reason this works is because when you compile this program, the compiler can match the different calls to the different function definitions. So effectively, there are two functions in this Java class, one without parameters and one with parameters.&lt;/p&gt;
&lt;p&gt;Python works differently. When you define a function, as you remember, you simply give a value to a variable. So if you do this in Python:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fun v1 called&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fun v2 called&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now there is &lt;em&gt;still&lt;/em&gt; only one version of &lt;code&gt;fun&lt;/code&gt;, and it's the last one that you defined. The second definition of &lt;code&gt;fun&lt;/code&gt; didn't overload the function, it replaced it.&lt;/p&gt;
&lt;p&gt;If you want your function to work the same way as the ones in the example above, you'll have to do it yourself:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fun v1 called&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fun v2 called&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, the gist is that Python does not have built-in overloading. Instead, you can assign a &lt;code&gt;None&lt;/code&gt; value for the parameter you want to overload and check for its existence yourself.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Well, there's a surprising amount of stuff in Python functions. The most important things you have to remember are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you want a variable number of positional arguments, use &lt;code&gt;args&lt;/code&gt;. If you want to variable number of named arguments, use &lt;code&gt;kwargs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Function definitions have a lot of syntax, but they're nothing "magic". They just simply create a function object and assign that value to a name, the function name.&lt;/li&gt;
&lt;li&gt;You can not overload a function in the Java-sense, but you can overload them in other ways, with named arguments, args, kwargs and your own separation logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do not use a mutable value as a default parameter value.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And finally, don't worry too much about it! =)&lt;/p&gt;</description><pubDate>Fri, 18 Jul 2014 15:00:00 +0200</pubDate></item><item><title>We've lost our top rating</title><link>https://shezi.de//posts/2014-01-23-weve-lost-our-top-rating.html</link><description>&lt;p&gt;Yesterday, &lt;a href="https://community.qualys.com/blogs/securitylabs/2014/01/21/ssl-labs-stricter-security-requirements-for-2014"&gt;Qualys released a new SSL test with updated guidelines&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Until that moment, our page at &lt;a href="https://sixsafe.com/"&gt;https://sixsafe.com/&lt;/a&gt; scored straight A in their test. Now we only score A-. &lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=sixsafe.com"&gt;Check it out for yourself!&lt;/a&gt; Then check your own page! The reason for this is that the new test includes new guidelines on Forward Secrecy. For some combinations of browsers by a certain OS company, we don't provide Forward Secrecy, leading to a downgrading.&lt;/p&gt;
&lt;p&gt;Unfortunately, there's not much I can do about this, because our web server does not support the necessary ciphers in the version we're using. So for the moment, we'll stay at A- and update our Forward Secrecy at some later point.&lt;/p&gt;</description><pubDate>Thu, 23 Jan 2014 14:01:35 +0100</pubDate></item><item><title>Mark your live deployments with userscripts</title><link>https://shezi.de//posts/2014-01-17-mark-your-live-deployments-with-userscripts.html</link><description>&lt;p&gt;I recently launched a web service at its own domain. I am actively developing the service on my local machine, so I always have at least two instances running at the same time.&lt;/p&gt;
&lt;p&gt;Thanks to that situation, I am scared whenever I change something that I'm doing the change to the wrong instance. Which would be bad.&lt;/p&gt;
&lt;p&gt;So I built myself a little tool to help me discern between the two: a bright red tag that shows me which the live version is. It's really simple with a &lt;a href="http://userstyles.org/"&gt;Userstyle&lt;/a&gt;. All I did was add a new userstyle for the domain name of my service:&lt;/p&gt;
&lt;pre&gt;@-moz-document domain("sixsafe.com") {

 body:before {

   content: "LIVE";

   font-size: 50px;
   background-color: red;
   color: #eee;

   margin: 0;
   padding: 100px 100px 5px 100px;

   position: absolute;
   top: -50px; left: -110px;

   -moz-transform: rotate(-45deg);
 }
}&lt;/pre&gt;
&lt;p&gt;You can see the result in the image: a bright red "LIVE" tag.&lt;/p&gt;
&lt;p&gt;This solution is really great for me: It's non-intrusive, so no-one else can see it. It's easy to install without changing the service and it's pretty obvious which instance I am working with. Everything I wanted!&lt;/p&gt;</description><pubDate>Fri, 17 Jan 2014 15:27:50 +0100</pubDate></item><item><title>Gamification 2.0</title><link>https://shezi.de//posts/2013-04-23-gamification-2-0.html</link><description>&lt;p&gt;A friend recently sent me a link to &lt;a href="http://www.ted.com/talks/jane_mcgonigal_gaming_can_make_a_better_world.html"&gt;Jane McGonigals excellent TED talk about Gamification&lt;/a&gt;. Watching it, I was reminded of the Mother Of All Gamification talks, &lt;a href="http://fora.tv/2010/07/27/Jesse_Schell_Visions_of_the_Gamepocalypse"&gt;Jesse Schell's Visions of the Gamepocalypse&lt;/a&gt;. I really enjoyed both of these talks, but I don't like their conclusions.
&lt;label for="mn-demo" class="margin-toggle"&gt;&amp;#8853;&lt;/label&gt;
&lt;input type="checkbox" id="mn-demo" class="margin-toggle"/&gt;
&lt;span class="marginnote"&gt;
  &lt;img class="size-thumbnail wp-image-174845321" alt="Even Coursera now offers courses on Gamification. Which is fitting, I guess, since university courses have been &amp;quot;gamified&amp;quot; with points and regular feedback for a long time." src="{{ site.baseurl }}/assets/Screen-Shot-2013-04-05-at-09.27.55-150x150.png" width="150" height="150" /&gt;&lt;br&gt;
   &lt;a href="https://www.coursera.org/course/gamification"&gt;Even Coursera now offers courses on Gamification.&lt;/a&gt; Which is fitting, I guess, since university courses have been "gamified" with points and regular feedback for a long time.[/caption]&lt;/p&gt;
&lt;p&gt;&lt;/span&gt;
&lt;/p&gt;&lt;/p&gt;
&lt;p&gt;What both Schell and McGonical want to do is make games that "trick" the player into doing something with himself or his surroundings at the same time. I don't think that's going to work. For one, because it's dishonest, not telling the user what you're actually trying to achieve with the game. But more importantly, because they are diluting the game design and experience with their ulterior motives. Did you play educational games when you were young? Probably &lt;a href="http://en.wikipedia.org/wiki/Blaster_Learning_System"&gt;Math Blaster!&lt;/a&gt; or &lt;a href="http://www.mariowiki.com/Mario_Teaches_Typing"&gt;Mario Teaches Typing&lt;/a&gt;? We had some of these when I was small and I never liked them. The reason is that you're playing a nice little game and suddenly something interrupts your flow with a math question. That's not a fun game mechanic. That's not any game mechanic, actually, that's just an annoying block between you and the game.&lt;/p&gt;
&lt;p&gt;There is a second mainstream meaning of the word gamification, which is "turning some task into a game". Wikipedia has a nice list of &lt;a title="Gamification Techniques on Wikipedia" href="http://en.wikipedia.org/wiki/Gamification#Techniques"&gt;things that belong into this category&lt;/a&gt;, i.e. things that people have tried to do. I think that's not the right way either, because I don't think that achievements (as funny as they may sometimes be) are the way to use the knowledge and power we have learnt from games to make the world a better or easier place. I also think that achievements, specifically, are a pretty poor game mechanic that's been used in recent years because they're so easy to make (however, &lt;a href="http://www.gamasutra.com/blogs/GregMcClanahan/20091202/3709/Achievement_Design_101.php"&gt;hard to get right&lt;/a&gt;), in contrast to meaningful in-game achievement without a badge, which is pretty hard to manufacture.&lt;/p&gt;
&lt;p&gt;However, I think that gamification is something quite different: Gamification is using the knowledge of game mechanics and techniques and how they work, and apply them to different areas than computer games. Note that I am speaking only of &lt;em&gt;knowledge&lt;/em&gt; of game mechanics, not game mechanics themselves. What I think games get right is finding and mining human traits like motivation and persistence. Games are exceedingly good at that, and good games even more so.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Leading&lt;/h2&gt;
&lt;p&gt;There's a nice explanation on Youtube how &lt;a title="Almost 20 minutes and you have to sign in. But watch it, seriously. There's more in there about how gamification works than in entire books I tried to read." href="http://www.youtube.com/watch?v=8FpigqfcvlM"&gt;Mega Man X gets it right&lt;/a&gt;. And did you know that the iconic first level of Super Mario Bros is designed &lt;i&gt;exactly&lt;/i&gt; to &lt;a href="http://www.auntiepixelante.com/?p=465"&gt;show you what you can do in the game&lt;/a&gt;? Jumping, hitting blocks, getting coins from � blocks, enemies that are dangerous and that you can defeat them by jumping on top of them. All of that from one single screen, and in less than ten seconds. Without a single text display.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;That&lt;/em&gt; is great gamification. They "gamified" their intro/tutorial and showed the player all these things effortlessly. Designing that level must have taken very long, but for the user it's so incredibly smooth. So that's one component of gamification for me: show instead of tell. Let the customer find out what's possible on their own, without bugging them, by making it easy, almost obvious what and where things are to find. Let's call this property "Leading", because a properly gamified systems leads the user towards actions that are beneficial.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Motivation&lt;/h2&gt;
&lt;p&gt;The second component of gamification that is most widely discussed is Motivation.

  &lt;label for="mn-demo" class="margin-toggle"&gt;&amp;#8853;&lt;/label&gt;
  &lt;input type="checkbox" id="mn-demo" class="margin-toggle"/&gt;
  &lt;span class="marginnote"&gt;

    &lt;span class="pointCounter"&gt;Points: &lt;span id="points"&gt;000000&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
    &lt;span class="achievementLocked" id="achievement"&gt;Achievement locked&lt;/span&gt;
    &lt;br/&gt;
    Can you scroll to a million points? Will you?
  &lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;Let's stay with Mario for a second: Mario has a coin counter and a points counter, and they are constantly increasing, all the time. Every time you do something, you get points or coins or both. So, in the simple gamification models of today, the designer would say "we need to motivate the player feedback to do something, so let's add points to it!" That &lt;em&gt;has&lt;/em&gt; been done, to great effect, and I guess it &lt;em&gt;can&lt;/em&gt; work—for the right kind of person. But I'm not that kind of person, and to me it feels very childish to attach meaningless points to meaningful tasks, &lt;a href="http://fora.tv/2010/07/27/Jesse_Schell_Visions_of_the_Gamepocalypse"&gt;which is exactly what Jesse Schell points at in his wonderful talk&lt;/a&gt;. There are systems, games, out there, that try to do this sucessfully (by &lt;a href="http://www.ted.com/talks/jane_mcgonigal_the_game_that_can_give_you_10_extra_years_of_life.html"&gt;Miss McGonical again&lt;/a&gt;), so they demonstrate that it works for some situations and for some people. However, the point counter in Mario isn't really the reason why one continues to play, is it? The point counter is merely an indicator that you're going in the right direction and how far you've gotten. As such, it's great, but it's &lt;em&gt;not&lt;/em&gt; the motivator. The motivator is something different: curiosity, urgency, "just one more level", epicness, or something like that. &lt;em&gt;This&lt;/em&gt; is what we have to take from games into the real world, not the stupid point counter. Of course, taking something so fluid and hard-to-grasp like motivation from a game and modelling a real-world task with that in mind is very hard. So it's natural to try out simple schemes first, like the point counter. But I think that a properly gamified system should motivate the user through its properties and its whole, not through meaningless points or achievements. So, let's take Motivation as our second component.&lt;/p&gt;
&lt;p&gt;There are a myriad ways to model and channel this motivation, like levels, stages, medals, progress, goodies or, yes, achievements. In fact, there are so many that finding the right combination for a specific situation can be pretty hard.&lt;/p&gt;
&lt;p&gt;Another important concept that helps form and channel user Motivation are reward schedules. And these are actually being taken into consideration when doing gamification. &lt;a href="http://venturebeat.com/2011/01/20/game-guru-jane-mcgonigal-says-gamification-should-be-hard-not-easy/"&gt;Some people proclaim that gamification should make tasks in the real world harder&lt;/a&gt;, but I don't think that's the case. Rather, it should move the challenge and the reward closer together, but keep the overall challenge/reward levels about the same. Which, thanks to human psychology, actually makes the task easier to do for a person. Brushing your teeth is a great example here (and the reason why Jesse Schell used it), because there's a challenge and a reward and they are very far apart from each other. The challenge is to brush your teeth twice or thrice a day. The reward is that your teeth will not develop problems and pain for you at some point in the future. So in this case, the world we live in is badly gamified, because the effort has to be put in right now and frequently from now on, while the reward is uncertain and a long way away. Humans are just not good at that kind of schedule, so here's something to be optimized. However, changing the reward schedule for brushing your teeth doesn't make it any harder or easier to do, it just makes it more natural for humans to do. In a way, a good reward schedule makes it easier to mine the motivation a person has for any goal and to apply that motivation in the right direction towards achieving that goal.&lt;/p&gt;
&lt;p&gt;&lt;a title="Jerry Seinfeld's productivity secret" href="http://lifehacker.com/281626/jerry-seinfelds-productivity-secret"&gt;There are very simple ways to do that&lt;/a&gt; and very complicated ways, but there are ways. I think we need to be getting better at finding them. Still, Motivation, one of the central points of Gamification.&lt;/p&gt;

&lt;h2&gt;Feedback&lt;/h2&gt;
&lt;p&gt;The third point I'd like to add to the essentials is Feedback. Humans love feedback and they need it to function properly. Giving people feedback on any process allows them to get better at it, so that's what they will do naturally. Let's look at our two examples from above. When you brush your teeth, there is some feedback that you did well: a fresh breath, smooth teeth, and a whiter smile. If you skip once or twice, that feedback will deteriorate, so once you stopped brushing your teeth the feedback won't work as well as before. However, there's also very little clear indication about &lt;em&gt;how&lt;/em&gt; good you did. There's no way to check that you brushed everywhere, that you brushed enough and that you brushed for long enough. So the feedback loop isn't very good, and that's probably the reason why you don't know anyone who's a master a brushing their teeth. Now let's analyze Mario. In a game of Mario, every action you take has a clear and consistent feedback value. Jumping on an enemy: happy sound, enemy dies and you get points (and sometimes even a shell). Bumping a � block: points, coins and another happy sound. Walk into an enemy: sad sound, your character falls of the screen and a live is deducted. Note that all of these actions are visible and clearly good or bad. If you do something that's very good, like finishing a level, a small fanfare is played and you get to see a nice little cut scene. Playing Mario is one giant session of trial and feedback. Some people say that &lt;a title="Shamus Young's opinion on the most innovative game in 2008" href="http://www.shamusyoung.com/twentysidedtale/?p=2048"&gt;positive feedback is all that makes a game good&lt;/a&gt;, and I tend to agree: give a person feedback on what they're doing and they'll get better and better at it.&lt;/p&gt;
&lt;p&gt;Most systems we use today are not very good at this. This is sad, because there are so many simple things one could add to most systems to make them give proper feedback, like status display, diagrams, scoreboards or suggestions for improvement. People can master any system as long as they are shown whether what they're doing is good or bad.&lt;/p&gt;

&lt;h2&gt;Narrative&lt;/h2&gt;
&lt;p&gt;
  &lt;label for="mn-demo" class="margin-toggle"&gt;&amp;#8853;&lt;/label&gt;
  &lt;input type="checkbox" id="mn-demo" class="margin-toggle"/&gt;
  &lt;span class="marginnote"&gt;
    &lt;img class="size-thumbnail wp-image-174845371" alt="Hero" src="{{ site.baseurl }}/assets/117611_7883-150x150.jpg" width="150" height="150" /&gt;&lt;br/&gt;
    Our hero begins his quest with a vow to finish this article.
  &lt;/span&gt;


  There is a fourth point that is central to any gamification effort you want to undertake, and that is narrative. People crave narratives of any kind. So much in fact, that they will create their own stories for &lt;a href="http://en.wikipedia.org/wiki/Tamagotchi"&gt;something as simple as a 32 x 16 monochrome display, showing a state machine with less than 16 states&lt;/a&gt;, making them bond with that state machine and get up in the middle of the night to 'feed' it. Note, however, that Tamagotchi is an excellent distillation of Feedback and thus masterfully gamified: people &lt;em&gt;want&lt;/em&gt; to carry the toy around and care for it every day, no matter its simplicity, creating Motivation and Narrative out of the players themselves.&lt;br /&gt;
Some of the most well-known and loved toys and games of the world are simple playgrounds for creating narratives: Lego, Barbie, Playmobil, The Sims and, of course, Minecraft. All these games offer is a rich environment for creating your own stories and adventures.&lt;br /&gt;
At the same time, even the most simple-minded games offer some kind of background story, whether that may be furious birds or supertechnology soldiers.&lt;/p&gt;
&lt;p&gt;The truth is that Narratives make it easier for people to understand a situation and muster up Motivation for it, even if the Narrative is quite thin. Even if you know that the princess is in another castle, here's a good and simple reason to slog through all these complicated worlds: find the princess and rescue her. Be a hero!&lt;/p&gt;
&lt;p&gt;Sometimes, team selections are used to create a simple but powerful Narrative: us against them. That seems to work for all recent 3d shooters, even if the teams are absolutely identical.&lt;/p&gt;
&lt;p&gt;In a way, Narrative is also something that McGonical and Schell point at and want to use as a tool to motivate the player. It's just that the methods they choose (point counters and badges) are quite crude and not very well-fitting, in my opinion. However, it's also true that Narrative is probably the hardest to add to any Gamification project that is not actually a game. How would you fit a Narrative (that is not totally moronic) around brushing your teeth or doing your taxes? I don't know! But figure it out and you'll have the power to help people do something important. And you'll find easy ways to collect your reward for that - even if it's just the knowledge that you're a hero!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So, to recap: &lt;strong&gt;Leading. Motivation. Feedback. Narrative.&lt;/strong&gt; Add these four to any system you are designing, and you will have a properly gamified system that will work very well.&lt;/p&gt;
&lt;p&gt;It's hard to tell how to actually use these things in any real product, but I think it's still worthwhile to start thinking about gamification on a broader scale.&lt;/p&gt;

&lt;style type="text/css" media="screen"&gt;
@font-face { font-family: 'Silkscreen'; src: url('http://shezi.de/wp-content/plugins/silkscreen/slkscr.ttf') format('truetype'); } .pointCounter { font-family: 'Silkscreen', 'Courier New'; background-color: white; margin-bottom: 0.8em; } .achievement { color: #f33; font-weight: bold; } .achievementLocked { color: #777; }
&lt;/style&gt;
&lt;p&gt;

  &lt;script src="http://code.jquery.com/jquery-2.1.4.min.js"&gt;&lt;/script&gt;
  &lt;script&gt;
var scrollPoints = 0;
var achieved = false;
var pointStats = $('#points');
console.log(pointStats);
function pad(num) {
  var s = (num % 1000000) +"";
  while (s.length &lt; 6)
    s = "0" + s;
  return s;
}

jQuery(function($) {
  var lastScroll = document.body.scrollTop;
            $(window).scroll(function(e) {
    var newScroll = window.pageYOffset;
            scrollPoints = scrollPoints + Math.abs(newScroll - lastScroll);
    if (!achieved) if(scrollPoints &gt; 1000000)
    {
      achieved = true;
      $('#achievement').removeClass('achievementLocked').addClass('achievement')
          .text('Achievement unlocked: Compulsive scroller');
    }
    pointStats.text(pad(scrollPoints));
    lastScroll = newScroll;
  });
});
&lt;/script&gt;</description><pubDate>Tue, 23 Apr 2013 21:45:22 +0200</pubDate></item><item><title>OAuth and Django — a cautionary tale</title><link>https://shezi.de//posts/2013-02-01-oauth-and-django-a-cautionary-tale.html</link><description>&lt;p&gt;Have you ever implemented OAuth authorization in any project of yours? Well, I have these last two weeks in a &lt;a href="http://bit-chest.com/"&gt;Django project I am doing&lt;/a&gt;, and it was not a nice experience. The following is going to be a bit of a rant against the state of authentication libraries, but take it as a cautionary tale and remember one thing:&lt;/p&gt;
&lt;blockquote class="posterous_short_quote"&gt;&lt;p&gt;Hours of doing can save you minutes of thinking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, let me tell you a little something about my experiences.
&lt;p&gt; The first thing you'll encounter on your quest is the choice between OAuth 1.0 and OAuth 2.0. There are voices for both sides, and they're both very convincing, so what you're going to do (if you're like me) is first implement OAuth1, then switch to OAuth2, then scrap that and go back to OAuth1. Because the first thing you'll try is to find and use a good OAuth1 library for Django, of which there are none. Oh, there are OAuth libraries all around, but I couldn't get any of them to work. So you'll say to yourself, screw this, I'm going to do OAuth2, &lt;em&gt;it's better anyway&lt;/em&gt;. Then you'll spend some time reading up on bearer tokens and refresh tokens and grant types and you'll come away more confused than enlightened. Then you'll play around a bit with the existing implementations like Google's wonderful &lt;a href="https://developers.google.com/oauthplayground/"&gt;OAuth playground&lt;/a&gt; and you'll say to yourself: &lt;em&gt;I can do this!&lt;/em&gt; And you'll start and do that, reading parts of the humongous spec on the side and notice more and more that there's loads of stuff in there that you don't even want to think about. Then you'll not get it done right, you won't find any client libraries for using it either, and then finally you'll read &lt;a href="http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/"&gt;Eran Hammer's rant over OAuth2&lt;/a&gt; and decide that it's really not in your best interest to implement OAuth2, and go back to searching for OAuth1 libraries you could use.
&lt;p&gt; So now you're stuck at that part again, but this time, you're determined. You're determined to find a stable and good implementation of OAuth1, that also offers 1.0a, because that's important for security. You'll skip &lt;a href="https://oauthlib.readthedocs.org/en/latest/"&gt;oauthlib&lt;/a&gt; because you don't trust it (a mistake, as you'll find out later) and also because the documentation is a bit sparse. Well-written but sparse. So instead you'll go directly for &lt;a href="http://oauth.googlecode.com/svn/code/python/"&gt;Leah Culvers six-years-old OAuth&lt;/a&gt; implementation. It's a bit crummy and you can see its age, but it's quite simple and adaptable, so you copy it verbatim into your project and start hacking away&amp;hellip;
&lt;p&gt; A mere two weeks later, you'll have touched every part of the library and added your own crumminess to it. You'll have an OAuth implementation that's half-working, is neither OAuth 1.0 nor OAuth 1.0a but somewhere in between, and you'll find new errors every other day, because you never took the time to read any of the OAuth tutorials there are &amp;mdash; because you really couldn't find good and simple ones, detailing the differences between OAuth versions. But, you'll have a working authentication scheme that uses signed requests and never exchanges secrets in the clear, which is a good thing all on its own.
&lt;p&gt; So let's examine the issues you will have had by the time you've come this far.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You did some reading at the beginning about OAuth and its versions, but couldn't find good enough material to make a decision. Go back and do that now, you'll regret it many times if you don't. Then pick one version and (in the case of OAuth2) one workflow and stick with that. You can extend later, if you need to.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;You didn't find any good, trustworthy-looking libraries, for neither version of OAuth. That's ok, just pick one and stick with it. Switching between libraries will not do you any good but burn your time away.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Once you've collected some experience and read enough of the materials and the spec, write down your findings and make it easier for others and for future-you to find them and learn from it.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, I'm in the third stage described. &lt;a href="https://github.com/shezi/django-oauth-starter"&gt;I've tried to address some of the issues I've found with current OAuth implementations and collect my knowledge about that stuff in code.&lt;/a&gt;&amp;nbsp;It's far from finished and far from done, but it will be useful once you start an OAuth1 implementation for yourself. You can find that code on GitHub along with some documentation I wrote, and I sincerely hope you can learn from my mistakes.&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;</description><pubDate>Fri, 01 Feb 2013 09:07:00 +0100</pubDate></item><item><title>Simplicity</title><link>https://shezi.de//posts/2012-11-08-simplicity.html</link><description>&lt;p&gt;A few days ago, a colleague pointed out to me that "simplicity" is not a feature. His reasoning behind this was that simplicity means different things for different persons, so simplicity can mean anything inside of a range of things.&lt;/p&gt;
&lt;p&gt;I don't agree with his statement at all, and I actually think that simplicity is the &lt;em&gt;only&lt;/em&gt; feature any program can have. In the widest sense, all that computers do is provide a given task in a simpler way, usually making that task faster and more accessible to different groups of users. Some of the times, making a task simpler makes the task possible in the first place.&lt;/p&gt;
&lt;p&gt;Even at the beginning, one of the first instances of a computer was the famous difference engine, invented by Charles Babbage. Its sole purpose? Computing tables of logarithms in a quick and reliable way. In other words: making finding a given logarithm simpler for the user. Note that the logarithm tables themselves are a way of making that task simpler in the first place.&lt;br /&gt;
After the difference engine came programmable looms by J. M. Jaquard, which made the production of complicated, patterned fabrics simpler.&lt;br /&gt;
Later, "real" programmable computers were invented by Konrad Zuse in Germany and von Neuman, Turing and others in Great Britain. Their use cases? Solving linear equations, doing the necessary computations to reverse code ciphers and computing ballistic tables. All of which make the underlying task simpler for the user. They also made these tasks a lot quicker and a lot more reliable, but these are merely side effects of making the tasks simpler for the user.&lt;/p&gt;
&lt;p&gt;Mainframes: Making it simpler to do complex calculations of any kind for many users.&lt;/p&gt;
&lt;p&gt;Home computers: Making it simpler to do complex calculations of any kind on your own.&lt;/p&gt;
&lt;p&gt;The internet: Making it simpler to retrieve information that is stored in different places.&lt;/p&gt;
&lt;p&gt;Email: Making it simpler to contact people that are not immediately available (i.e. making it simpler to send letters).&lt;/p&gt;
&lt;p&gt;Even "modern" services and gadgets like Google (making it simpler to find some websites that store information you might be interested in), Facebook (making it simpler to have a homepage, share photos and see what others are doing), Twitter (making it simpler to tell everyone who's interested what you're doing) or Apple computers (making it simpler to use all the awesome stuff that nerds built for themselves) are built on that one guiding principle of simplicity.&lt;/p&gt;
&lt;p&gt;Note that oftentimes, with simplicity come other benefits like scalability or even availability, which help adoption and development of even newer, even simpler products.&lt;/p&gt;
&lt;p&gt;But the main thing that software, that technology does, is making stuff simple. I can't think of a single example of a product or service that is &lt;em&gt;not&lt;/em&gt; making something simpler.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Shameless plug: I'm doing &lt;a title="Bit Chest -- the simplest file archive solution" href="http://bit-chest.com/"&gt;Bit Chest&lt;/a&gt; at the moment, which is built around being a simple and safe file archiving solution.&lt;/p&gt;&lt;/blockquote&gt;</description><pubDate>Thu, 08 Nov 2012 15:05:00 +0100</pubDate></item><item><title>Over-specialization is the curse of our civilization</title><link>https://shezi.de//posts/2012-11-06-over-specialization-is-the-curse-of-our-civil.html</link><description>&lt;p&gt;I'm sick of the distinction between blue-collar and white-collar workers.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The blue-collar people think they're hot shit because they solve the hard problems. They know how to build. They look down on white-collar workers who are only solving trivial problems. Blue-collar workers build the real things. They don't have time to learn about spreadsheets or a wretched scheme like Wall street. Without them, a nation can never be successful.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The white-collar workers think they're hot shit because they're the closest to the trade. They know to perfect a business process. They look down on blue-collar workers who have no idea how the produce is actually used. White-collar workers build the real things. They don't have time to learn about crop rotation or wretched schemes like shift work. Without them, a nation can never be successful.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;You're both wrong. You must do both or you'll be irrelevant (somewhere).&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Over-specialization is the curse of our civilization. Stratification breeds condescension. The only remedy is for all workers to do everything.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size: xx-small;"&gt;This post is an answer to the similarly incendiary &lt;a href="http://www.onebigfluke.com/2012/11/over-specialization-is-curse-of-our.html"&gt;Over-specialization is the curse of our profession&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description><pubDate>Tue, 06 Nov 2012 07:25:00 +0100</pubDate></item><item><title>Great presentations: Steli Efti on Happiness as an Entrepreneur</title><link>https://shezi.de//posts/2012-08-09-great-presentations-steli-efti-on-happiness-a.html</link><description>&lt;p&gt;I just watched &lt;a href="https://elasticsales.com/blog/2012/08/03/entrepreneurial-depression-happiness/"&gt;Steli Efti's TEDx talk on happiness as an entrepreneur&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;It's a great presentation: moving, emotional and with a great message.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;However, if you look only at the technical aspects of his talk, it should have been quite mediocre. He didn't use any slides, he walked around all the time, he had to hold his own microphone. All stuff that we're told is bad for a presentation. But still, it was really good to look at and go with it.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Why? Because Mr Efti has a message that he cares about. And that is the only thing that can make a good presentation.&lt;/p&gt;</description><pubDate>Thu, 09 Aug 2012 09:02:00 +0200</pubDate></item><item><title>The last of the first</title><link>https://shezi.de//posts/2012-07-13-the-last-of-the-first.html</link><description>&lt;p&gt;I just threw out the rest of my first start-up's stuff. It was a big box of flyers we had printed and wanted to distribute among students. Most of the flyers had handwritten codes on them that would allow people to try out our product, &lt;a href="http://partycode.de/"&gt;http://partycode.de/&lt;/a&gt; for free. We hand-wrote them because it was cheaper.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Back then, I wasn't afraid of ordering 5000 printed flyers. I also wasn't afraid of throwing out the first batch because I had a spelling error on them, and ordering a new batch. I wasn't afraid of an evening of writing codes with friends.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;But throwing them out...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I had kept the stuff for a year, before I finally liberated myself. It's not that I didn't know that it was over, after all, I had shut down all work on that enterprise, as well as the next one, but still, throwing away all that stuff that I worked so hard for felt strange.&lt;/p&gt;</description><pubDate>Fri, 13 Jul 2012 21:11:14 +0200</pubDate></item><item><title>Validation</title><link>https://shezi.de//posts/2012-05-16-validation.html</link><description>&lt;p&gt;You know the situation: You've written a great new product, you've just released it (much to your own relief, everything seems to be working) and now you're contemplating the next steps.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;A little while later, you've done your homework, you have a Facebook page, you have a Twitter account, you have put your release out there on Reddit and Hacker News and StumbleUpon and even Delicious. And somehow, nothing happened. Maybe you've sold a copy or two, but annoyingly, the masses don't seem to be noticing your nice little App at all.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;At first, your spirits don't mind. These people, they'll come! They'll find my pages, they'll notice my marketing and see my ads. Maybe after the next version? Maybe if you tweak your analytics just a little bit more...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Some days pass, and nothing happens. Your spirits really do start to mind, and indeed they fall at the same rate that your helplessness rises. You sit around sulking and think about what you did wrong.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;And all of a sudden, this happens:&lt;/p&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;a href="http://sheziimport.files.wordpress.com/2012/05/screen_shot_2012-05-16_at_11-5.png"&gt;&lt;img alt="Screen_shot_2012-05-16_at_11" src="{{ site.baseurl }}/assets/screen_shot_2012-05-16_at_11-5.png?w=300" width="500" height="116" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;At that moment, you know what's missing: Validation! You needed someone to tell you that some of your stuff is interesting. That at least someone is looking at it.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Now, you can continue!&lt;/p&gt;</description><pubDate>Wed, 16 May 2012 09:53:38 +0200</pubDate></item><item><title>Simple APIs</title><link>https://shezi.de//posts/2012-04-26-simple-apis.html</link><description>&lt;p&gt;I like simple APIs.&lt;/p&gt;
&lt;p&gt;Simple in the sense of small, with few options and few available calls, as opposed to monstrous can-do-everything-APIs. Alright, so maybe I should say small from now on.&lt;/p&gt;
&lt;p&gt;Before I tell you why I like small APIs more than bigger ones, let me first tell you why I don't like large ones. There are actually severe drawbacks associated with larger APIs and a bigger selection of available methods and options, but not many people seem to notice, or care.&lt;/p&gt;
&lt;p&gt;The first thing that large APIs do with me is make me feel lost. That is obviously true for really large APIs (like the &lt;a href="http://msdn.microsoft.com/en-us/library/ws8s10w4.aspx"&gt;MFC&lt;/a&gt;, with which I had first contact when I was fifteen and which probably delayed my programming career by a few years), because there's just so much to see and so much to do that you suffer from the well-known &lt;a href="https://en.wikipedia.org/wiki/The_Paradox_of_Choice:_Why_More_Is_Less"&gt;choice paradox&lt;/a&gt; as well as sensory overload -- you simply don't know where to look at first, so instead of finding an entry point, I just gave up. But this also applies to smaller libraries where many options are exposed that look like very similar at first glance. Case in point here is the wonderful &lt;a href="http://www.icodeblog.com/2010/10/07/cloning-uiimagepickercontroller-using-the-assets-library-framework/"&gt;ELCImagePicker&lt;/a&gt;, that I'm using on Imprys. I don't want to bash on this library, it's quite excellent indeed, and I would have had a hard time implementing it myself.&lt;/p&gt;
&lt;p&gt;However, and this is the second things that bugs me about large APIs, every time I use the ELCImagePicker, I am left unsure whether I did everything right. Instead of providing a single entry point with a single method and a single callback protocol, I have to compose two classes myself. &lt;i&gt;Two classes&lt;/i&gt;. This is about as simple as it gets, but &lt;b&gt;still&lt;/b&gt;, it's more complicated than I would like.&lt;/p&gt;
&lt;p&gt;Call me lazy (and laziness is probably one part of it), but the more important part is decisions. Programs and program libraries are collections of decisions. The more decisions are encapsulated in a library, the more I can feel safe that these decisions are right in that context, because someone (and probably someone smarter than me) thought about that decision, weighed the pro's and con's of each possible option and then just made the decision. So I don't have to.&lt;/p&gt;
&lt;p&gt;This is why I like small APIs: They make me feel safe in my choices.&lt;/p&gt;
&lt;p&gt;So what can you do about large APIs? That question is actually quite a philosophical topic, because in a way, each component in a computer program, down to the language you are using, is an API. I think that the only way to handle each and every of these APIs is to make your own, for the situation that you're in. Wrapping lower-level APIs, more or less, because that's what writing programs is, anyway.&lt;/p&gt;
&lt;p&gt;So, create a DSL, even if that DSL is "just" a class with two methods. In fact, I think that a class with two methods is probably a really, really good API, and if it fits your program, you should use that. Your program should look like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;#import &amp;quot;mylib.h&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;mylib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mylib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mainmenu&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;mylib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mainprogram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That, right there, is an awesome program. It's powerful, it does what it's supposed to, I can understand it. And it uses all of its underlying APIs.&lt;/p&gt;
&lt;p&gt;And that's why I like simple APIs.&lt;/p&gt;</description><pubDate>Thu, 26 Apr 2012 20:54:23 +0200</pubDate></item><item><title>Starting up again</title><link>https://shezi.de//posts/2012-04-25-starting-up-again.html</link><description>&lt;p&gt;After my vacation and another week of hanging around, I started up again today. Feels good to be back in the programmer's seat.
&lt;div&gt;&lt;a href="http://imprys.com/"&gt;Imprys&lt;/a&gt;&amp;nbsp;can now do graphics, although they aren't saved in the right format yet, and they don't get exported to HTML, neither. Before that can happen, I will have to restructure my output writer to use a templating system, most probably the wonderfully simple&amp;nbsp;&lt;a href="https://github.com/groue/GRMustache"&gt;Mustache&lt;/a&gt;.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;One more annoyance: I've tried to use the Apple-supplied&amp;nbsp;&lt;a href="http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIImagePickerController_Class/UIImagePickerController/UIImagePickerController.html"&gt;UIImagePicker&lt;/a&gt;. There are some strange design decisions in there, for example, in the iPad, you can only show the UIImagePicker in its own UIPopoverController.&lt;/div&gt;
&lt;div&gt;Also, it doesn't allow picking more than one image at a time, which feels a bit silly when you try to add ten or twenty images to your presentation. So I've looked around the web and, behold, there is an alternative implementation on Github,&amp;nbsp;&lt;a href="https://github.com/elc/ELCImagePickerController"&gt;ELCImagePickerController&lt;/a&gt;, which works beautifully. Until the moment when you find out that this image picker needs to have the Location API enabled and needs access to it. Mind you, none of the images on my iPad is location-encoded, because I have locations disabled. Still, to read the album headings, location API needs to be enabled. Which is a bit of a shame, because for those users that don't have that enabled, I'll have to fall back on the regular UIImagePicker.&lt;/div&gt;&lt;/p&gt;</description><pubDate>Wed, 25 Apr 2012 18:37:26 +0200</pubDate></item><item><title>Charlemagne: Keep the fire burning | The Economist</title><link>https://shezi.de//posts/2011-10-06-charlemagne-keep-the-fire-burning-the-economi.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;
&lt;h3&gt;Charlemagne&lt;/h3&gt;
&lt;h3&gt;Keep the fire burning&lt;/h3&gt;
&lt;h3&gt;Why Germany seems not to want a quick fix for the euro crisis&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;    &lt;img title="" src="{{ site.baseurl }}/assets/20111001_EUD000_0.jpg" height="281" alt="" width="500" /&gt;          &lt;/div&gt;
&lt;p&gt;TO HEAR Germany say no to fiscal stimulus, no to boosting the euro zone’s rescue fund, no to joint Eurobonds, one begins to wonder: does it really want to resolve the euro zone’s crisis? Of course we do, most Germans would reply. Is not the chancellor, Angela Merkel, the first to declare that “if the euro fails, Europe will fail”? Is Germany not the main provider of rescue loans to Greece, Ireland and Portugal?&lt;/p&gt;
&lt;p&gt;All this is true. Yet Mrs Merkel seems to lack a sense of urgency. Despite the world’s calls for action, she does not believe in bold strokes—be it letting Greece default, or issuing Eurobonds to mutualise governments’ debt. Only a slow, step-by-step approach will work. In other words, the pain, austerity and market turmoil will go on for the foreseeable future.&lt;/p&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li class="4 last"&gt;Economist.com/blogs/charlemagne&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.economist.com/node/21531032?fsrc=scn/tw/te/ar/keepthefireburning"&gt;economist.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Even though it pains me to bring attention to a rather silly opinion piece on the Economist, I just have to point out a few things about the piece linked above.&lt;/p&gt;
&lt;p&gt;First of all, having a sense of urgency is not the same as panicking, which, to a German, is how many of the bold-stroked measures implemented by the USA or Great Britain feel like. "How much money can we find to just give away?" is not a position I would like my government to take.&lt;/p&gt;
&lt;p&gt;Second, I object to the off-handed description of Germanys institutions. The "complex federal system" isn't any more complex than any other federal system, like, say, that of the USA. A sceptical public is a good thing for a democracy. Coalition politics are about finding compromises and will only appear messy to someone coming from an either/or-system, which feels very primitive to many Europeans. And finally, "jealous institutions such as the constitutional court"? Really? Jealous? A working, well-respected institution that upholds the central law code of a country is "jealous"? This, as well, coming from a resident of a country where the constitutional court feels like just another venue for political puppery, doesn't actually feel like an insult and instead more like a praise.&lt;/p&gt;
&lt;p&gt;Third, there is growth. I don't want to comment more on that topic than saying that "X is weakening growth, thus X is bad" is not a valid argument until you have proven that growth is important. I know that many people assume this automatically in the United States, but it's not a given in other places. And, at least from time to time, you should think about it.&lt;/p&gt;
&lt;p&gt;Then there are the weak arguments. Charlemagne quotes that one problem with seeing slow steps as a problem "is that market panic can become self-fulfilling, in many different ways". This is supposed to be an argument against a transaction tax. A tax that would temper the possibility for panic and create a cushion for when it occurs is held as an argument against that tax.&lt;/p&gt;
&lt;p&gt;And, finally, there is the obsession with the german language. Yes, "Schulden" does share the same stem as "Schuld", just like, for example, "candidate" and "candid" share the same stem, even though most candidates aren't. Or "champion" and "Kampf" (which means struggle or fight). Language is a strange beast and goes wounded ways. Bringing up the similarity between Schulden and Schuld (which, by the way, also means "debt" in this context) feels like filling up for missing real arguments. Oh, and also, bringing up the protestant past for a country where less than half of its inhabitants believe in any spiritual force isn't really a strong point, either.&lt;/p&gt;
&lt;p&gt;Actually, the more I read this piece, the more I get the feeling of a very strong cognitive dissonance. On the one hand, Germany is bad for letting "the fires burn", on the other hand, Germany seems to be very stable and financially secure at the moment. It's sad that even such a careful observer as Charlemagne seems to be likes to forgo arguments for rhetoric once in a while.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Thu, 06 Oct 2011 08:26:00 +0200</pubDate></item><item><title>A very nice experience</title><link>https://shezi.de//posts/2011-09-29-a-very-nice-experience.html</link><description>&lt;p&gt;Today, I joined Bitbucket. A friend invited me. And, I must say, I am very pleasantly surprised. I have used Github before, but I just can't get warm with git, and of course that colours the experience for Github.
&lt;p&gt; However, the experience for BitBucket is also very friendly, and very simple. They even send you a welcome-email with pointers to the next steps, including a tutorial for newbies and a tutorial for Subversion switchers.
&lt;p&gt; And one factor, naturally, is always price. BitBucket has the best price you can get, because it's free. Other than GitHub, you can have as many repositories as you want, and as long as you don't have more than five collaborators, everything's free.
&lt;p&gt; If you're interesting in trying out a DVCS, or find a nice host for Mercurial, I urge you to try BitBucket.&lt;/p&gt;&lt;/p&gt;</description><pubDate>Thu, 29 Sep 2011 21:16:55 +0200</pubDate></item><item><title>Foreign code</title><link>https://shezi.de//posts/2011-09-22-foreign-code.html</link><description>&lt;p&gt;On&amp;nbsp;&lt;a href="http://www.sea-of-memes.com/LetsCode33/LetsCode33.html"&gt;last weeks&lt;/a&gt;'&amp;nbsp;&lt;a href="http://www.sea-of-memes-com/"&gt;Sea Of Memes&lt;/a&gt;&amp;nbsp;the topic of using libraries, working together with other people and copyright issues came up.
&lt;div&gt;Then, a few days later,&amp;nbsp;&lt;a href="http://www.shamusyoung.com/twentysidedtale/?p=13366"&gt;Shamus Young discussed the article&lt;/a&gt;&amp;nbsp;on his blog&amp;nbsp;&lt;a href="http://www.shamusyoung.com/twentysidedtale/"&gt;Twenty Sided&lt;/a&gt;.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;I disagree, with both of them, on almost all the issues raised.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;The first issue is "working with strangers". Here's the quote:&lt;/div&gt;
&lt;div style="font-family:georgia;"&gt;
&lt;table style="background-color:rgb(254,244,218);"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style="font-family:georgia;"&gt;I'm also trying to learn things about graphics programming and game programming. You don't really learn something unless you try to do it yourself. Using a big multi-platform library or an existing game engine wouldn't teach me much.&lt;/p&gt;
&lt;p style="font-family:georgia;"&gt;Then there's the problem of debugging. Things go wrong and you need to isolate the problem down to a particular piece of code. It helps enormously if you've written it all yourself. If other people are working in the same area, it's practically impossible to know whether the bug was caused by the thing you just changed, or by someone else's change. It also helps that I know my own style and the kinds of mistakes I tend to make.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;I commend mgoodfel on his journey to learn an interesting skill and work his way towards a fully self-written game engine. I'd like to do that myself some day, so I understand that he wants to write most of the code himself. That shouldn't stop him from collaborating with other people towards this goal, however. In fact, most of the things I learned didn't come from myself sitting in the basement doing it alone, but from talking to other learners about it and talking them through my thought process, oftentimes finding crucial flaws in my thinking. Of course, listening to someone else's thought process and finding their flaws, all the while seeing a wholly different position. In a way, Sea Of Memes is mgoodfels way of achieving this, whereby he writes down his thought process, then waits for comments from his readers. He even states this specific goal in&amp;nbsp;&lt;a href="http://www.sea-of-memes.com/LetsCode29/LetsCode29.html"&gt;Part 29&lt;/a&gt;:&lt;/div&gt;
&lt;p&gt;
&lt;div style="font-family:georgia;"&gt;
&lt;table style="background-color:rgb(254,244,218);"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style="font-family:georgia;"&gt;The writeups revolve around the code. A part gets done when I get a piece of code running, not on any regular schedule. And then I wait for feedback, either downloads of the demo, or comments or both.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;But still, to me, it doesn't feel like collaboration, it's more like an improv performance, where the audience is allowed from time to time to yell a word or two about the next piece mgoodfel should perform and how it should look like.&lt;/div&gt;
&lt;div&gt;What's even worse, there is a simple system that would address the issues cited above, as well as mine, and that is GitHub. The workflow for an open-source GitHub project is like this: You create a repository, then push your initial state there. Everyone can see this code and do a "fork", i.e. copy the repository into their own account, then check it out and work on it. Meanwhile, you've worked on it yourself, pushing code to the public front end only at times you choose (kind of like putting them into a Zip file on your blog, only that it's easier to see what changed). If someone else wants to take your code and work towards a different direction, that's fine because you won't even notice. If someone wants to contribute to &lt;i&gt;your&lt;/i&gt; code, they push a change that implements what they want to show/give you and open a "Pull Request". This means you get a message that someone wants to contribute, a patch in regular diff format and a button that says "Do it!". You'd then read through their patch, thinking about the consequences the integration has for your code, thereby &lt;i&gt;making it your own&lt;/i&gt;, and if you're satisfied with the integration, you pull it into your repository, merging the change. If you're not satisfied, you simply ignore the request. Happens all the time.&lt;/div&gt;
&lt;div&gt;The point I'm trying to make is this: Pull Requests open your code up for collaboration if you choose to integrate. If not, you still saw someone else's thinking, which is always a good thing.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;Oh, and you know what: The internet doesn't care when you're awake. It doesn't matter when you pull, when you push and when you work on your code, because everything is asynchronous. You and all of your potential collaborators, work at their own times, paces, and work styles.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;The second issue that comes up is libraries. Here's what Shamus Young writes about using libraries:&lt;/div&gt;
&lt;div&gt;&lt;span style="font-family:Verdana, Tahoma, Helvetica, Arial, sans-serif;line-height:22px;"&gt;Using code from strangers is always a gamble. Sometimes, on rare circumstances (perhaps only a few occasions in your career) the clouds will open and you’ll find yourself in a beam of sunlight, looking at a link to the perfect library while angelic music plays.&amp;nbsp;&lt;a href="http://www.shamusyoung.com/twentysidedtale/?p=12304" style="color:rgb(74,126,185);text-decoration:none;"&gt;It does happen&lt;/a&gt;. But more often than not, you run into the usual chain of&amp;nbsp;&lt;a href="http://www.shamusyoung.com/twentysidedtale/?p=9557" style="color:rgb(74,126,185);text-decoration:none;"&gt;dependencies, missing files, lack of documentation, version incompatibilities, inscrutable bugs, missing features, and poor programming interface&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;To me, that sounds as if Mr Young was burned by some libraries or code snippets he tried to use for his own project, Project Frontier. It sounds as if he sees libraries as a time-saving tool. When he needs some functionality in his program, he thinks about the quickest way to achieve that functionality, which might be (a) writing it yourself or (b) taking code from someone else to do what you need. If you see it like that, then yes, every library is a gamble, where you wager time and hope to win back that time by not having to write the code yourself, i.e. you invest some time into integrating and probably bugfixing and working with the library, and earn back the time by saving implementation time. If the library is good, you have made a net time win, where you have the functionality quicker with option (b) than option (a). If you find out that the library isn't for you and "give up", your investment is lost and you'll have to start anew.&lt;/div&gt;
&lt;div&gt;That's a bleak way to look at libraries, I think. To me, libraries are about&amp;nbsp;&lt;a href="http://ries.typepad.com/ries_blog/2010/03/experience-the-focus-effect.html"&gt;focus&lt;/a&gt;, and not losing it. To me, the choice isn't "how much time will this cost?", instead it's "how well does this fit into my vision?". So, I ask myself: "Do I really want to write a JPEG loader?" and "Do I really want to mess with certificates myself?" (a topic I will discuss in the post after this one), or "Do I want to write a Minecraft chunk loader?". Some of the times, the answer is no, some of the times the answer is yes. When the answer is no, I'd rather spend an hour or two banging some library in shape and massaging my own code instead of looking at&amp;nbsp;&lt;a href="http://libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature"&gt;byte-descriptions and file signatures&lt;/a&gt;, until the question becomes "Do I want to maintain this ****** library or find some other way?", when I probably give up.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;The third issue mgoodfel brings up is copyright and patents. This is a hairy one, and I can understand that he's afraid of being sued. On the other hand, what could anyone win from suing him? He doesn't appear to have a lot of money and if you wanted to stop him from distributing or using some piece of code, it'd probably be more effective to ask him. Most of the disputes in that area are settled out of court, anyway, and I don't think one should be scared by the way big companies with huge cash reserves behave towards each other. Also, note that Samsung and Apple are still cooperating on other areas of their businesses.&lt;/div&gt;
&lt;div&gt;Perhaps I am too young, or haven't been burned by issues like these, or I am in a jurisdiction where it's easier to handle, but I don't think a programmer should care too much about patents, lawsuits and unintentional breach of copyright. In fact, my feeling is quite eloquently summarized by pud, when he writes "&lt;a href="http://pud.com/post/10103947044/fucking-sue-me"&gt;Fucking Sue Me&lt;/a&gt;".&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;So, there you have it. My personal, completely unfounded opinion on why you should do everything differently than you do. Note, again, that these are my opinions, my suggestions for thinking about it, not requests that anyone change anything about the way they do what they want.&lt;/div&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;</description><pubDate>Thu, 22 Sep 2011 10:25:51 +0200</pubDate></item><item><title>What’s wrong with this code, really? | CV Mountain.com</title><link>https://shezi.de//posts/2011-09-16-whats-wrong-with-this-code-really-cv-mountain.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;img alt="Media_httpcvmountainc_xbcja" height="179" src="{{ site.baseurl }}/assets/media_httpcvmountainc_xbcja-scaled500.png" width="449" /&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://cvmountain.com/2011/09/whats-wrong-with-this-code-really/"&gt;cvmountain.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;A very nice example of bad, but correct code and the rationale for replacing it with a cleaner, better version.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Fri, 16 Sep 2011 17:25:00 +0200</pubDate></item><item><title>A powerful demonstration of Ghostery</title><link>https://shezi.de//posts/2011-09-15-a-powerful-demonstration-of-ghostery.html</link><description>&lt;p&gt;I have used the excellent Ghostery for a few weeks now. What it does is it blocks a long list of web tracking and ad network services to make your browsing more private. Every time Ghostery blocks a service, it shows a little bubble with a message what was blocked. This way, you can easily see, who's tracking your browsing how. Also, it's simply interesting.
&lt;p&gt; For example, this is yahoo.com:
&lt;div class="p_embed p_image_embed"&gt;&lt;img alt="Screen_shot_2011-09-15_at_10" height="97" src="{{ site.baseurl }}/assets/screen_shot_2011-09-15_at_10-3.png" width="160" /&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;google.com doesn't show any warnings, so I guess they track you on their servers, which you cannot block, of course. &lt;br /&gt;For comparison, here's techdirt.com, anandtech.com and engadget.com&lt;/p&gt;
&lt;p&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;img alt="0screen_shot_2011-09-15_at_10" height="207" src="{{ site.baseurl }}/assets/0screen_shot_2011-09-15_at_10-3.png" width="152" /&gt;&lt;img alt="1screen_shot_2011-09-15_at_10" height="97" src="{{ site.baseurl }}/assets/1screen_shot_2011-09-15_at_10-3.png" width="150" /&gt;&lt;img alt="2screen_shot_2011-09-15_at_10" height="107" src="{{ site.baseurl }}/assets/2screen_shot_2011-09-15_at_10-3.png" width="201" /&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;It's amazing that techdirt places a whole ten different services on their page that can track you. However, the funniest thing happened today, when a website I visited used Clicky (getclicky.com). I hadn't heard of it before, so I went and took a look. Unfortunately, they use similar addresses to their tracking scripts on their web page, which Ghostery blocked. The feature comparison chart is especially nice:&lt;/p&gt;
&lt;p&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/09/3screen_shot_2011-09-15_at_10-3.png"&gt;&lt;img alt="3screen_shot_2011-09-15_at_10" height="294" src="{{ site.baseurl }}/assets/3screen_shot_2011-09-15_at_10-3.png?w=300" width="500" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Well. That's a hard choice, there... =)&lt;/p&gt;&lt;/p&gt;</description><pubDate>Thu, 15 Sep 2011 08:40:03 +0200</pubDate></item><item><title>Focus puller - Wikipedia, the free encyclopedia</title><link>https://shezi.de//posts/2011-09-14-focus-puller-wikipedia-the-free-encyclopedia.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;
&lt;div&gt;From Wikipedia, the free encyclopedia&lt;/div&gt;
&lt;p&gt;A &lt;b&gt;focus puller&lt;/b&gt;, or 1st assistant &lt;a href="http://en.wikipedia.org/wiki/Focus_puller#" title="Camera operator"&gt;camera operator&lt;/a&gt;, is a member of a &lt;a href="http://en.wikipedia.org/wiki/Focus_puller#" title="Film crew"&gt;film crew&lt;/a&gt;’s camera department whose primary responsibility is to maintain image &lt;a href="http://en.wikipedia.org/wiki/Focus_puller#" title="Sharpness (visual)"&gt;sharpness&lt;/a&gt; on whatever subject or action is being filmed.&lt;/p&gt;
&lt;p&gt;“Pulling focus” refers to the act of changing the &lt;a href="http://en.wikipedia.org/wiki/Focus_puller#" title="Camera lens"&gt;lens&lt;/a&gt;’ focus distance setting in correspondence to a moving subject’s physical distance from the focal plane. For example, if an actor moves from 8 m away from the focal plane to 3 m away from the focal plane within a shot, the focus puller will change the distance setting on the lens during the take in precise correspondence to the changing position of the actor. Additionally, the focus puller may shift focus from one subject to another within the frame, as dictated by the specific requirements of the shot (&lt;a href="http://en.wikipedia.org/wiki/Focus_puller#" title="Cinematic techniques"&gt;cinematic techniques&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;A good focus puller will have an intimate knowledge of cinematographic and optical theory. Depending on the parameters of a given shot, there is often very little room for error. As such, the role of a focus puller is extremely important within the realm of a film production; a “soft” image will, in most circumstances, be considered unusable, since there is no way to fix such an error in post-production. One must also consider that an actor may not be able to duplicate his or her best performance in a subsequent take, so the focus puller is expected to perform flawlessly on every take. Because of these factors, most production personnel consider the focus puller to have the most difficult job on set.&lt;sup&gt;[&lt;i&gt;&lt;a href="http://en.wikipedia.org/wiki/Focus_puller#" title="Wikipedia:Citation needed"&gt;&lt;span title="This claim needs references to reliable sources from August 2011"&gt;citation needed&lt;/span&gt;&lt;/a&gt;&lt;/i&gt;]&lt;/sup&gt;&lt;/p&gt;
&lt;div&gt;Retrieved from "&lt;a href="http://en.wikipedia.org/wiki/Focus_puller"&gt;http://en.wikipedia.org/wiki/Focus_puller&lt;/a&gt;"&lt;/div&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://en.wikipedia.org/wiki/Focus_puller"&gt;en.wikipedia.org&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Today I learned that there are many, many people, measurements, time and work involved in making sure that films stay in focus. It's a lot more complicated than I thought and explains long filming schedules and budgets. Amazing!&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 14 Sep 2011 23:30:00 +0200</pubDate></item><item><title>Software’s Receding Hairline - raganwald's posterous</title><link>https://shezi.de//posts/2011-09-11-softwares-receding-hairline-raganwalds-poster.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;      &lt;a href="http://raganwald.posterous.com/javas-comb-over"&gt;&lt;img class="posterous_download_image" src="{{ site.baseurl }}/assets/url.jpg" border="0" height="313" width="466" /&gt;&lt;/a&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://raganwald.posterous.com/javas-comb-over"&gt;raganwald.posterous.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Interesting comparison between a comb-over and software development in the sense that both are the result of years of waiting for the right moment to change something.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 11 Sep 2011 10:07:00 +0200</pubDate></item><item><title>Today I learned something</title><link>https://shezi.de//posts/2011-09-01-today-i-learned-something.html</link><description>&lt;p&gt;So, yesterday was a good day, because I finally got a push to do something about my lingering ambitions. And that's what I did today. In the process, I learned that the MacOS X is not as stable as I thought.
&lt;p&gt; As I was playing around with a simple application for monitoring the system clipboard (called Pasteboard in MacOS X), I managed to corrupt the clipboard. After my application quit, any other application that would try to access the clipboard would go into an infinite loop.
&lt;p&gt; I have put some proof-of-concept code on GitHub, under &lt;a href="https://github.com/shezi/ClipCrash"&gt;https://github.com/shezi/ClipCrash&lt;/a&gt;&lt;/p&gt;&lt;/p&gt;</description><pubDate>Thu, 01 Sep 2011 13:09:00 +0200</pubDate></item><item><title>The Million-Dollar-Question and its consequences</title><link>https://shezi.de//posts/2011-08-31-the-million-dollar-question-and-its-consequen.html</link><description>&lt;blockquote&gt;
&lt;div&gt;[...]
&lt;p&gt;80% of the people I draw up plans for &amp;ndash; plans that would clearly work &amp;ndash; don&amp;rsquo;t act on them.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s like everyone fantasizes about&amp;hellip; whatever&amp;hellip; but once their fantasies start to become reality, they piss their pants and self-sabotage.&lt;/p&gt;
&lt;p&gt;[...]&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.sebastianmarshall.com/the-million-dollar-question"&gt;sebastianmarshall.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;
&lt;p&gt; I just read this very poetic and well-written post by Sebastian Marshall about why we don't act on our urges to do what we need to do to be successful. Everyone who has ever thought about being an entrepreneur or "do something cool along the way" should read it. And while you're at it, you should read some more by Mr Marshall, because he writes an awful lot of good things.
&lt;p&gt; The piece resonated with me because I am the same way. You probably know the feeling, thinking about this passion that you have, that crazy thing you always wanted to try. You get that suppressed smile on your face and you can almost not stop talking about "the thing", even though everyone thinks you're crazy.
&lt;p&gt; Perhaps you've even made a plan, how you would make enough time while still working. How you'd make sure that you can pay the rent every month, because you're not crazy enough to do &lt;em&gt;that&lt;/em&gt;&amp;nbsp;kind of thing full-time. And it might even be a good plan, with numbers and dates and milestones and possible problems and all the other adult things a plan needs to have to persuade anyone that the thing you're planning is not entirely crazy.
&lt;p&gt; You might even have started to work on your plan, perhaps a prototype, or technology research or market research. You might have the numbers to prove that you can do it and that you'd earn money with it.
&lt;p&gt; You might even have talked to others about the plan, perhaps found collaborators, co-conspirators. You might've had a few appointments, talked with them how great it would be and how easy it would be to implement and just do it. You could have had a few beers on the occasion, celebrated your courage and strengthened it with your future business colleagues.
&lt;p&gt; But then, something else happened instead. The first milestone came and went. &lt;br /&gt;The prototype was never made into a real product, or perhaps it wasn't even finished. Or, you know, something in the family came up, or you really needed some safe money, right now, because your car broke down or something like that. Or maybe the kitchen needs fixing, or one of your friends asked for help with moving. Or, hey, look at that, there's already a new set of pictures on reddit that you could look at all day.
&lt;p&gt; I know how that goes. Because I did.
&lt;p&gt; Let me explain!
&lt;p&gt; Almost six months ago now, I quit one of my jobs. At the time, I had two half-time employments, and when one of them didn't work out as planned, I took the plunge and quit. That wasn't an easy thing to do because I lost quite a lot in the process, but I did all the things I suggested above. I had dreams, and ideas, and plans even. I had talked about it with my girlfriend and my parents, and they were all for trying it. I even talked to my girlfriends father and mother about it, and even &lt;em&gt;they&lt;/em&gt;&amp;nbsp;were in favor of trying my hand at being self-employed. And so I did it. Since I only had to work 20 hours a week, which would cover all my living expenses nicely, I even had a lot of time on my hands to be a successful entrepreneur.
&lt;p&gt; And, you know, I even went so far as to create a product, Partycode, which is still live and reachable, and which has, to date, not had a single customer. Because when it came to marketing, telling people about the product, going all-in, so-to-speak, I stopped. And so did all two of my fellows, even though we had had great plans for the product.
&lt;p&gt; But, I wasn't deterred. I still had the time and I still wanted to do it. So I drew up more plans. Forged new alliances, found something really neat that I still believe would have worked, if I just could bring myself to do it. And so this wonderful idea, this great product, has never seen the light of day, except in the fragments of our design documents.
&lt;p&gt; What do I do instead? Well, there was the Steam Summer Sale, which was nice. Oh, and then there were all these parties, and the big family event we held. I might have watched one or two TED talks, and perhaps some OpenYale courses. And, of course, Hacker News and Reddit are always there for me when I don't want to do anything else. Nowadays, it's so easy to distract yourself from this lingering thought that you should actually be working...
&lt;p&gt; But, at least I think I can add something to Mr Marshall's million dollar question, because, for me, I think I know the reason.
&lt;p&gt; The reason is potential. Right now, I'm a potential entrepreneur, a potential genius and millionaire and the ideas I have are potentially wonderful, successful and great, and potentially, everyone loves me for producing such a great thing. Actually starting to work on the ideas rubs that golden glow off and makes them less potential, less potent even, although in the process, they would get real. It would take away the fairy dust and add adult things, like milestones and feature lists and press releases, things that don't fit into the perfect vision of the ideal elegant product. And it would also carry the risk of failure. All of a sudden, you're not a millionaire, instead you're just a programmer who has tried to be more than he thinks he is. You're just a nerd than cannot understand why other people are not as nerdy, why no-one would even want to try out revolution you produced. You're just an idiot with no idea about marketing apart from Google AdWords. You're just a lazy guy, sitting at home all day, thinking about how great he might be one day, without ever trying. You're just, let's face it, a loser who should go back to working for the man, because the man knows better anyway.
&lt;p&gt; It's not that easy to face your dreams' destruction. Perhaps even more so when some of your ideas have already not worked.
&lt;p&gt; But writing all this down, I realize that it's worth it. Sitting on your dreams until you're old and grey, letting time get away from you, that's not the way to go. I don't want to look back at my life with rueful eyes, thinking that I _should_ have tried it. I want to look back, thinking: I did it.
&lt;p&gt; So, I'm going to start following my own advice. In a first, painful step, I deleted all the games on this computer and blocked some websites that might distract me from the things I really want to do.
&lt;p&gt; And I'm going to start posting here, every day. Come back tomorrow and you'll see...&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;</description><pubDate>Wed, 31 Aug 2011 23:03:00 +0200</pubDate></item><item><title>::Maximise:: - weblog of Max Tobiasen</title><link>https://shezi.de//posts/2011-08-31-maximise-weblog-of-max-tobiasen.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;a name="7638995285375284997"&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h3&gt;
&lt;p&gt; Moving a boat&lt;br /&gt;
     &lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;      This is a story about what it takes to start a company. It may not seem like it, but it is.
&lt;p&gt;I live on a boat in a harbour. It's an amazing place with just the right mix of artists, yacht owners and fishermen. As in all small communities there's a strong invisible pecking order. The fishermen are on top. They are the real men. They have survived force gale 12 storms in the bay of Biscay, they haul cod out of the freezing water with their bare hands in the winter, they move crates around the harbour like children moving toys across the lawn. And, of course they know all there is to know about boats. When it comes to boats they are the unquestionable authority. They have seen it all before, and they are always right. They are where you go for buying, selling, moving and fixing boats. &lt;/p&gt;
&lt;p&gt;I like the fishermen. Without them I would have no idea how to fix all the things that inevitably break on my old boat. And I can tell they enjoy sharing their vast knowledge. But to them I'm not a real man. I'm an academic. I can't lift the heavy crates, I've never been in a real storm. And I can't haul cod, not even in the summer. To them I'm a boy. &lt;/p&gt;
&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;      &lt;em&gt;posted by Max Tobiasen at &lt;a href="http://www.maximise.dk/blog/2009/04/moving-boat.html" title="permanent link"&gt;00:33&lt;/a&gt;&lt;/em&gt;        &lt;/p&gt;
&lt;div&gt;&lt;a href="http://www.blogger.com/email-post.g?blogID=7758596&amp;amp;postID=7638995285375284997" title="Email Post"&gt;&lt;img src="{{ site.baseurl }}/assets/icon18_email.gif" height="13" alt="" width="18" /&gt;&lt;/a&gt;
&lt;p&gt;&lt;a href="http://www.blogger.com/email-post.g?blogID=7758596&amp;amp;postID=7638995285375284997" title="Email Post"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=7758596&amp;amp;postID=7638995285375284997" title="Edit Post" style="border:none;"&gt;&lt;img src="{{ site.baseurl }}/assets/icon18_edit_allbkg.gif" height="18" alt="" width="18" /&gt;&lt;/a&gt;
&lt;p&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=7758596&amp;amp;postID=7638995285375284997" title="Edit Post" style="border:none;"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.maximise.dk/blog/2009/04/moving-boat.html"&gt;maximise.dk&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Nicely written story/parable about what it takes to do it.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 31 Aug 2011 21:33:00 +0200</pubDate></item><item><title>Is /r/programming still /r/'programming'?</title><link>https://shezi.de//posts/2011-08-30-is-rprogramming-still-rprogramming.html</link><description>&lt;p&gt;Today, I have to make a confession: I read reddit. Quite often, indeed. I do have my haggles with it, but for the time being, it's one of the best sources of fresh information there is.
&lt;p&gt; My most frequented subreddit is /r/programming &lt;br /&gt;I often see comments on this subreddit that say something to the effect of "this is not programming, go away to another subreddit!" (happened to the article below this one an hour ago). &lt;br /&gt;The description of this subreddit on the right hand side is also pretty clear on the topic ("PROGRAMMING!"), basically saying that only posts with code are worthy programming posts.
&lt;p&gt; However, the current top link is about an MITM attack, the second about VIM, then there are links about DOS attacks, encryption on paper (which to me sound like the opposite of programming), the UI of InfoQ and censorship. So there are quite a lot of posts that aren't specifically about programming as such, even though they are related with it and interesting for programmers, which is why they were submitted to /r/programming in the first place, I suppose.
&lt;p&gt; Don't misunderstand me, I'm not complaining about the diversity of the links, in fact, I'm enjoying it.
&lt;p&gt; The question I have is: Should we not embrace this diversity and relax our standards of "not programming"?&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;</description><pubDate>Tue, 30 Aug 2011 10:51:25 +0200</pubDate></item><item><title>Defeating the MPAA with their own weapons</title><link>https://shezi.de//posts/2011-08-30-defeating-the-mpaa-with-their-own-weapons.html</link><description>&lt;p&gt;I was thinking a bit this morning about how one could use the MPAAs own tactics to defend against lawsuits by them. According to the DMCA (and similar directives in Europe), it is illegal to circumvent "technical protection measures" against copying and accessing copied material. Since this law applies to everyone, not only the consumers of protected material, let's do a little thought experiment:
&lt;p&gt; Imagine that I go to a popular file sharing site and upload a file that contains a movie. Since I don't want anyone to sue me for doing that, I encrypt the movie using a standard software like GPG. This encryption should be so strong that not even the NSA can open my file and sue me (and if they do, we'd know a lot more about what the NSA knows about breaking GPG), so I should be safe from the movie studios, as the file I uploaded is in no way copyright protected as it stands. Now, of course I want my friends to be able to download and watch the movie, so I give them the key to the file, which could also be rather trivial. For example, the uploader's name, or the movie title, or the release date of the film or anything else. &lt;br /&gt;I suppose I could even package the key and the encrypted file into an archive together. It wouldn't be much different from a BluRay disc, which contains a lot of software and some data that allows a suitably set-up device to decrypt the disc. &lt;br /&gt;Well, but now I have added a "technical protection measure" and an access control to my file. Circumventing that measure, i.e. decrypting the disc when you are not the intended recipient would be illegal by the DMCA. Of course, I also added a file to the archive with a license stating that you can only use the file supplied for private use inside the state of Nigeria and only if you are the son of the overthrown dictator, but I guess most movie pirates wouldn't care about that. &lt;br /&gt;As soon as I receive a suit for copyright infringement under the DMCA I can now apply that same right to me: Obviously the suing agency must have circumvented my copy protection measure and this be in breach of the DMCA. You could even argue that my encryption, which uses gold-standard GPG with a key length of 4096 bit is a lot more effective than the skimpy BluRay protection measures, so I should be entitled to a lot more damage compensation.
&lt;p&gt; What do you think?&lt;/p&gt;&lt;/p&gt;</description><pubDate>Tue, 30 Aug 2011 09:29:27 +0200</pubDate></item><item><title>Functional Programming Is Hard,That's Why It's Good</title><link>https://shezi.de//posts/2011-08-25-functional-programming-is-hardthats-why-its-g.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;
&lt;p&gt;Odds are, you don’t use a functional programming language every day. You probably aren’t getting paid to write code in Scala, Haskell, Erlang, F#, or a Lisp Dialect. The vast majority of people in the industry use OO languages like Python, Ruby, Java or C#–and they’re happy with them. Sure, they might occasionally use a “functional feature” like “blocks” now and then, but they aren’t writing functional code.&lt;/p&gt;
&lt;p&gt;And yet, for years we’ve been told that functional languages are awesome. I still remember how confused I was when I first read &lt;a href="http://catb.org/~esr/faqs/hacker-howto.html"&gt;ESR’s famous essay&lt;/a&gt; about learning Lisp. Most people are probably more familiar with &lt;a href="http://www.paulgraham.com/avg.html"&gt;Paul Graham’s “Beating The Averages”&lt;/a&gt; which makes the case that:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;But with Lisp our development cycle was so fast that we could sometimes duplicate a new feature within a day or two of a competitor announcing it in a press release. By the time journalists covering the press release got round to calling us, we would have the new feature too.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A common thread among people proselytizing functional programming is that learning this new, functional language is “good for you”; almost like someone prescribing 30m in the gym a day will “make you fit,” but it also implies difficulty and dedication. Haskell, Ocaml, and Scala are different from Lisp in that they have a certain notoriety for being very hard to learn. Polite people call this “being broad &amp;amp; deep”. Less polite people call it “mental masturbation” or “academic wankery” or just plain “unnecessary.” I submit that this difficulty is a familiar situation, and it’s a strong indicator that learning one of these languages will make you more productive and competent at writing software.&lt;/p&gt;
&lt;h3&gt;Your First Time Wasn’t Gentle&lt;/h3&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://dave.fayr.am/posts/2011-08-19-lets-go-shopping.html"&gt;dave.fayr.am&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Nice!&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Thu, 25 Aug 2011 19:40:00 +0200</pubDate></item><item><title>The Sagan Series (part 6) - End of an Era: The Final Shuttle Launch - YouTube</title><link>https://shezi.de//posts/2011-08-21-the-sagan-series-part-6-end-of-an-era-the-fin.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;      [youtube http://www.youtube.com/watch?v=3wJYpRJQVbo]
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.youtube.com/watch?v=3wJYpRJQVbo&amp;amp;feature=player_embedded#!"&gt;youtube.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Every time humanity stretches itself, it receives a jolt of productivity, that can carry for centuries.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 21 Aug 2011 16:14:00 +0200</pubDate></item><item><title>Papers from Philosophical Transactions of the Royal Society, fro (download torrent) - TPB</title><link>https://shezi.de//posts/2011-07-22-papers-from-philosophical-transactions-of-the.html</link><description>&lt;p&gt;
    -----BEGIN PGP SIGNED MESSAGE-----&lt;br&gt;Hash: SHA1&lt;br&gt;&lt;br&gt;This archive contains 18,592 scientific publications totaling 33GiB, all from Philosophical Transactions of the Royal Society and which should be  available to everyone at no cost, but most have previously only been made available at high prices through paywall gatekeepers like JSTOR. Limited access to the  documents here is typically sold for $19USD per article, though some of the older ones are available as cheaply as $8. Purchasing access to this collection one article at a time would cost hundreds of thousands of dollars. Also included is the basic factual metadata allowing you to locate works by title, author, or publication date, and a checksum file to allow you to check for corruption.
  &lt;/p&gt;
&lt;p&gt;
  ef8c02959e947d7f4e4699f399ade838431692d972661f145b782c2fa3ebcc6a sha256sum.txt&lt;/p&gt;

&lt;p&gt;
    I've had these files for a long time, but I've been afraid that if I published them I would be subject to unjust legal harassment by those who profit from controlling access to these works. I now feel that I've been making the wrong decision. On July 19th 2011, Aaron Swartz was criminally charged by the US Attorney General's office for, effectively, downloading too many academic papers from JSTOR. Academic publishing is an odd system &amp;mdash; the authors are not paid for their writing, nor are the peer reviewers (they're just more unpaid academics), and in some fields even the journal editors are unpaid. Sometimes the authors must even pay the publishers. And yet scientific publications are some of the most outrageously expensive pieces of literature you can buy. In the past, the high access fees supported the costly mechanical reproduction of niche paper journals, but online distribution has mostly made this function obsolete. As far as I can tell, the money paid for access today serves little significant purpose except to perpetuate dead business models.&lt;br&gt;The "publish or perish" pressure in academia gives the authors an impossibly weak negotiating position, and the existing system has enormous inertia. Those with the most power to change the system--the long-tenured luminary scholars whose works give legitimacy and prestige to the journals, ratherthan the other way around--are the least impacted by its failures. They are supported by institutions who invisibly provide access to all of the resources they need. And as the journals depend on them, they may ask for alterations to the standard contract without risking their career on the loss of a publication offer. Many don't even realize the extent to which academic work is inaccessible to the general public, nor do they realize what sort of work is being done outside universities that would benefit by it. Large publishers are now able to purchase the political clout needed to abuse the narrow commercial scope of copyright protection, extending it to completely inapplicable areas: slavish reproductions of historic documents and art, for example, and exploiting the labors of unpaid scientists. They're even able to make the taxpayers pay for their attacks on free society by pursuing criminal prosecution (copyright has classically been a civil matter) and by burdening public institutions with outrageous subscription fees. Copyright is a legal fiction representing a narrow compromise: we give up some of our natural right to exchange information in exchange for creating an economic incentive to author, so that we may all enjoy more works. When publishers abuse the system to prop up their existence, when they misrepresent the extent of copyright coverage, when they use threats of frivolous litigation to suppress the dissemination of publicly owned works, they are stealing from everyone else. Several years ago I came into possession, through rather boring and lawful means, of a large collection of JSTOR documents. These particular documents are the historic back archives of the Philosophical Transactions of the Royal Society &amp;mdash; a prestigious scientific journal with a history extending back to the 1600s. The portion of the collection included in this archive, ones published prior to 1923 and therefore obviously in the public domain, total some 18,592 papers and 33 gigabytes of data. The documents are part of the shared heritage of all mankind, and are rightfully in the public domain, but they are not available freely. Instead the articles are available at $19 each--for one month's viewing, by one person, on one computer. It's a steal. From you. When I received these documents I had grand plans of uploading them to Wikipedia's sister site for reference works, Wikisource &amp;mdash; where they could be tightly interlinked with Wikipedia, providing interesting historical context to the encyclopedia articles. For example, Uranus was discovered in 1781 by William Herschel; why not take a look at the paper where he originally disclosed his discovery? (Or one of the several follow on publications about its satellites, or the dozens of other papers he authored?) But I soon found the reality of the situation to be less than appealing: publishing the documents freely was likely to bring frivolous litigation from the publishers. As in many other cases, I could expect them to claim that their slavish reproduction &amp;mdash; scanning the documents &amp;mdash; created a new copyright interest. Or that distributing the documents complete with the trivial watermarks they added constituted unlawful copying of that mark. They might even pursue strawman criminal charges claiming that whoever obtained the files must have violated some kind of anti-hacking laws. In my discreet inquiry, I was unable to find anyone willing to cover the potentially unbounded legal costs I risked, even though the only unlawful action here is the fraudulent misuse of copyright by JSTOR and the Royal Society to withhold access from the public to that which is legally and morally everyone's property. In the meantime, and to great fanfare as part of their 350th anniversary,the RSOL opened up "free" access to their historic archives&amp;mdash;but "free" only meant "with many odious terms", and access was limited to about 100 articles. All too often journals, galleries, and museums are becoming not disseminators of knowledge&amp;mdash;as their lofty mission statements suggest&amp;mdash;but censors of knowledge, because censoring is the one thing they do better than the Internet does. Stewardship and curation are valuable functions, but their value is negative when there is only one steward and one curator, whose judgment reigns supreme as the final word on what everyone else sees and knows. If their recommendations have value they can be heeded without the coercive abuse of copyright to silence competition. The liberal dissemination of knowledge is essential to scientific inquiry. More than in any other area, the application of restrictive copyright is inappropriate for academic works: there is no sticky question of how to pay authors or reviewers, as the publishers are already not paying them. And unlike 'mere' works of entertainment, liberal access to scientific work impacts the well-being of all mankind. Our continued survival may even depend on it. If I can remove even one dollar of ill-gained income from a poisonous industry which acts to suppress scientific and historic understanding, then whatever personal cost I suffer will be justified&amp;mdash;it will be one less dollar spent in the war against knowledge. One less dollar spent lobbying for laws that make downloading too many scientific papers a crime. I had considered releasing this collection anonymously, but others pointed out that the obviously overzealous prosecutors of Aaron Swartz would probably accuse him of it and add it to their growing list of ridiculous charges. This didn't sit well with my conscience, and I generally believe that anything worth doing is worth attaching your name to. I'm interested in hearing about any enjoyable discoveries or even useful applications which come of this archive.&lt;br&gt;---- Greg Maxwell - July 20th 2011 &lt;a href="http://thepiratebay.org/torrent/6554331/Papers_from_Philosophical_Transactions_of_the_Royal_Society__fro/mailto:gmaxwell@gmail.com"&gt;gmaxwell@gmail.com&lt;/a&gt;&lt;br&gt;Bitcoin: 14csFEJHk3SYbkBmajyJ3ktpsd2TmwDEBb&lt;br&gt;-----BEGIN PGP SIGNATURE-----&lt;br&gt;Version: GnuPG v1.4.11 (GNU/Linux)&lt;br&gt;iEYEARECAAYFAk4nlfwACgkQrIWTYrBBO/pK4QCfV/voN6IdZRU36Vy3xAedUMfzrJcAoNF4/QTdxYscvF2nklJdMzXFDwtF=&lt;br&gt;YlVR&lt;br&gt;
    -----END PGP SIGNATURE-----
  &lt;/p&gt;
&lt;footer&gt;via &lt;a href="http://thepiratebay.org/torrent/6554331/Papers_from_Philosophical_Transactions_of_the_Royal_Society__fro"&gt;thepiratebay.org&lt;/a&gt;&lt;/footer&gt;

&lt;p&gt;It looks as if things are really going to get moving for the scientific community. This is another push for better, free access to scientific papers and journals in the form of 33GiB of papers which should be free. I wholeheartedly agree with everything Mr Maxwell writes, and if you care about the quality of science, so should you!&lt;/p&gt;</description><pubDate>Fri, 22 Jul 2011 08:28:00 +0200</pubDate></item><item><title>When Employees Misinterpret Managers // ben's blog</title><link>https://shezi.de//posts/2011-07-21-when-employees-misinterpret-managers-bens-blo.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;span style="cursor:pointer;"&gt;Time is money, so I went and bought a Rolex&lt;br /&gt;—Wiz Khalifa, &lt;em&gt;Phone Numbers&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When I ran Opsware, we had the non-linear quarter problem also known affectionately as &lt;em&gt;the hockey stick&lt;/em&gt;. The hockey stick refers to the shape of the revenue graph over the course of a quarter. Our hockey stick was so bad that one quarter, we booked 90% of our new bookings on the last day of the quarter. Sales patterns like this make it difficult to plan the business and are particularly harrowing when you are, as we were, a public company.&lt;/p&gt;
&lt;p&gt;Naturally, I was determined to straighten out the hockey stick and bring some sanity to the business. I designed an incentive for sales people to close deals in the first two months of the quarter by issuing bonuses for deals in those months. As a result, the next quarter became slightly more linear, and slightly smaller than anticipated – deals just moved from the 3&lt;sup&gt;rd&lt;/sup&gt; month to the first two of the following quarter.&lt;/p&gt;
&lt;p&gt;[...]
&lt;/p&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://bhorowitz.com/2011/07/20/when-employees-misinterpret-managers/"&gt;bhorowitz.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Really interesting comment on what to manage towards.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Thu, 21 Jul 2011 09:14:00 +0200</pubDate></item><item><title>Shower</title><link>https://shezi.de//posts/2011-07-20-shower.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/07/media_httppepelsbeygi_ecejg-scaled1000.jpg"&gt;&lt;img alt="Media_httppepelsbeygi_ecejg" height="313" src="{{ site.baseurl }}/assets/media_httppepelsbeygi_ecejg-scaled1000.jpg?w=300" width="500" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://pepelsbey.github.com/shower/"&gt;pepelsbey.github.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Shower is a great template for presentations in the web. Works on all important browsers and looks very neat.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 20 Jul 2011 12:41:00 +0200</pubDate></item><item><title>Googles long-term memory</title><link>https://shezi.de//posts/2011-06-19-googles-long-term-memory.html</link><description>&lt;p&gt;Recently, I made an interesting discovery regarding Googles long-term memory. I was shopping for some RAM modules for my notebook and I wanted to find out, which modules exactly I needed. So, I googled. I found the correct type, used a price-comparison website, ordered a few and that was that.
&lt;p&gt; But, a few days later, this appeared on a page I was visiting:
&lt;div class="p_embed p_image_embed"&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/06/screen_shot_2011-06-05_at_17-2.png"&gt;&lt;img alt="Screen_shot_2011-06-05_at_17" height="519" src="{{ site.baseurl }}/assets/screen_shot_2011-06-05_at_17-2.png?w=288" width="500" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;A bit later, this one:&lt;/p&gt;
&lt;p&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/06/0screen_shot_2011-06-05_at_17-2.png"&gt;&lt;img alt="0screen_shot_2011-06-05_at_17" height="134" src="{{ site.baseurl }}/assets/0screen_shot_2011-06-05_at_17-2.png?w=300" width="500" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;And then this:&lt;/p&gt;
&lt;p&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/06/1screen_shot_2011-06-05_at_17-2.png"&gt;&lt;img alt="1screen_shot_2011-06-05_at_17" height="156" src="{{ site.baseurl }}/assets/1screen_shot_2011-06-05_at_17-2.png?w=300" width="500" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;And then these:&lt;/p&gt;
&lt;p&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/06/screen_shot_2011-06-06_at_11-4.png"&gt;&lt;img alt="Screen_shot_2011-06-06_at_11" height="142" src="{{ site.baseurl }}/assets/screen_shot_2011-06-06_at_11-4.png?w=300" width="500" /&gt;&lt;/a&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/06/screen_shot_2011-06-07_at_00-3.png"&gt;&lt;img alt="Screen_shot_2011-06-07_at_00" height="126" src="{{ site.baseurl }}/assets/screen_shot_2011-06-07_at_00-3.png?w=300" width="500" /&gt;&lt;/a&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/06/screen_shot_2011-06-16_at_20-2.png"&gt;&lt;img alt="Screen_shot_2011-06-16_at_20" height="150" src="{{ site.baseurl }}/assets/screen_shot_2011-06-16_at_20-2.png?w=300" width="500" /&gt;&lt;/a&gt;&lt;img alt="Screen_shot_2011-06-17_at_18" height="396" src="{{ site.baseurl }}/assets/screen_shot_2011-06-17_at_18-4.png" width="396" /&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Now, that's funny. Obviously, after I had bought some memory modules, I was definitely in the market for some more. Or maybe a hard drive (which wouldn't even fit into the notebook). It looks like Google has an impressive long-term memory, storing my "interesting" searches for quite a while, because I only got them to stop by opting-out of ad-tracking completely. No, I find this a waste of resources, because there's a difference here between events that occur only once, like shopping for hardware, and events that occur more often, like shopping for books or such things. And I think it's strange that Google cannot differentiate between the two. After all, this should show up rather obviously in click statistics.
&lt;p&gt; However, this is the first time such a strange campaign targeted myself. Have any of you seen such things?&lt;/p&gt;&lt;/p&gt;</description><pubDate>Sun, 19 Jun 2011 22:36:22 +0200</pubDate></item><item><title>Python Idioms and Efficiency Suggestions</title><link>https://shezi.de//posts/2011-06-16-python-idioms-and-efficiency-suggestions.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;Check out this website I found at &lt;a href="http://jaynes.colorado.edu/PythonIdioms.html"&gt;jaynes.colorado.edu&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Some very nice and very effective Python tips. Sweet!&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Thu, 16 Jun 2011 09:37:00 +0200</pubDate></item><item><title>Ask Slashdot: Best Certifications To Get? - Slashdot</title><link>https://shezi.de//posts/2011-06-09-ask-slashdot-best-certifications-to-get-slash.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;
&lt;p&gt;A bit off topic, but you triggered something I've been thinking about for a couple of years.  That "spark" is fluency.&lt;/p&gt;
&lt;p&gt;I swtiched jobs from being a computer programmer to being an ESL teacher in Japan.  Japan is somewhat famous for churning out students who know a lot *about* English, but can't order a drink at Mac Donald's.  We used to have a name for those kinds of people with regard to programming languages: language laywers.  They can answer any question you put to them *about* a programming language, but couldn't program to save their life.  These people often make it past job interviews easily, but then turn out to be huge disappointments when they actually get down to work.  I've read a lot about this problem, but the more I look at it, the more I realise that these disabled programmers are just like my students.   They have a vocabulary of 5000 words, know every grammar rule in the book but just can't speak.&lt;/p&gt;
&lt;p&gt;My current theory is that programming is quite literally writing.  The vast majority of programming is not conceptually difficult (contrary to what a lot of people would have you believe).  We only make it difficult because we suck at writing.  The vast majority of programmers aren't fluent, and don't even have a desire to be fluent.  They don't read other people's code.  They don't recognise or use idioms.  They don't think *in the programming language*.  Most code sucks because we have the fluency equivalent of 3 year olds trying to write a novel.  And so our programs are needlessly complex.&lt;/p&gt;
&lt;p&gt;Those programmers with a "spark" are programmers who have an innate talent for the language.  Or they are people who have read and read and read code.  Or both.  We teach programming wrong.  We teach it the way Japanese teachers have been teaching English.  We teach about programming and expect that students will spontaneously learn to write from this collection of facts.&lt;/p&gt;
&lt;p&gt;In language acquisition there is a hypothesis called the "Input Hypothesis".  It states that *all* language acquisition comes from "comprehensible input".  That is, if you hear or read language that you can understand based on what you already know and from context, you will acquire it.  Explanation does not help you acquire language.  I believe the same is true of programming.  We should be immersing students in good code.  We should be burying them in idiom after idiom after idiom, allowing them to acquire the ability to program without explanation.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://ask.slashdot.org/comments.pl?sid=2198700&amp;amp;cid=36293622"&gt;ask.slashdot.org&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Wonderful comparison between learning natural languages and learning programming languages.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Thu, 09 Jun 2011 08:03:00 +0200</pubDate></item><item><title>I am scared</title><link>https://shezi.de//posts/2011-05-24-i-am-scared.html</link><description>&lt;p&gt;I guess it all started when I was a little kid. I didn't even notice, back then.
&lt;p&gt; When I was about six, I learned about how the world has enough water for everyone, but unfortunately it's all salt water. I also learned about evaporation, and that you basically get distilled water for free with it. So, my little kids mind went to work combining these two effects and created a scheme to water the worlds deserts. It's quite simple, actually. You take a few sheets of glass, mount them at an angle, add a few pipes as well as a pump adding seawater under the panes, and instantly you can start harvesting freshwater suitable for nurturing crops. I was sure, with this simple scheme you could generate enough fresh water to make the deserts green. After all, that's what happens in clouds, too, right? And actually, that scheme does work: &lt;a href="http://en.wikipedia.org/wiki/Solar_desalination"&gt;http://en.wikipedia.org/wiki/Solar_desalination&lt;/a&gt;
&lt;p&gt; But, as you probably know, I did not succeed in my grandiose plans, and thus the deserts still remain unwatered. Why did I not do anything? Because I was scared. Scared by my own courage, scared by all the things no one else would do to help me, scared of leaving my cozy little middle-class comfort zone. So I remained inert, even though the little voice in my head mocked me for not doing what I knew was necessary.
&lt;p&gt; Fifteen years later I finished school and went to university. A wonderful environment for anyone who wants to learn at crazy speeds, and so I did. I met lots of people, some of whom had started a business when they were still in school. I knew that this was what I wanted to do, too. Start a business, be productive, be successful, be happy. But there was so much else to do. Studying. Girls. Exams. Learning. And even though I read all the essays by Paul Graham, witnessed many Internet business started and ended, saw the bubble rise and burst and I was always this close to doing it myself. I could see the changes coming, could see all the niches still unfulfilled, some of them as large as whole industries were before. I felt the changes in people's behaviors when they found what the Internet and modern computers could do for them, and I knew that the technology would revolutionize most areas of business there are. I talked with my friends about how we would be part of that revolution, how we would kill the old beasts and be masters of the new economy. We had plans, we knew what we had to do, and in retrospect, we were actually right there, too. Most of the ideas were realized sooner or later by people who were scarily successful with them. &lt;br /&gt;But I didn't do anything about it. I was scared. Scared to start without a degree, scared to be responsible for myself, scared to lose the fine little progress I had made for myself. I found arguments, easy and lazy arguments why I shouldn't start a business, why I should be careful and think about it some more. And yet, the little voice was still there, still mocking, still nagging.
&lt;p&gt; It stayed that way for almost ten more years.
&lt;p&gt; But then, somehow, the excuses started dropping away. I had a finished degree, I was safely employed, health-insured, in a stable relationship, had travelled far and wide, I had started doing sports, had bought all the things I never thought I'd buy and I had safely arrived in the adult world. I even started learning to play music to take my mind away from all the things I should be doing.
&lt;p&gt; And then, finally, the last piece fell into place. I plunged. I quit my PhD studies, stayed employed half-time and started my first business. The voice in my head was excited: I was actually doing what I was supposed to do, and what so many had showed me before.
&lt;p&gt; And now, I am two months in. I am more scared as ever, but I can see the things that scare me now, clearly. I'm still scared by all the red tape, of which I have to take care. I'm still scared of all the lawyers, accountants, guidelines, tax laws, chambers of commerce, customers, competitors and customers that are already involved in my business and who will be involved in the future. And I've only scratched the surface. But everything is so clear, now. There's no voice mocking me for my failure to launch, because I launched. There's no boss in my face to tell me what to do and what not to do, because I am my own boss. The scare is now in front of me, so I can cope with it.
&lt;p&gt; It has taken twenty-five years, almost all my life, but I have finally arrived in the place I wanted to reach since I was a child. I took the muffled scares that surrounded me and placed them crystal clear in front of me. Now I can deal with them.
&lt;p&gt; And so can you!&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;</description><pubDate>Tue, 24 May 2011 20:49:23 +0200</pubDate></item><item><title>Dropbox is a huge torrent swarm</title><link>https://shezi.de//posts/2011-05-24-dropbox-is-a-huge-torrent-swarm.html</link><description>&lt;div&gt;Funny, how the world turns, isn't it? Some weeks ago I wrote the following article. Then this happened:&amp;nbsp;&lt;a href="http://razorfast.com/2011/04/25/dropbox-attempts-to-kill-open-source-project/"&gt;http://razorfast.com/2011/04/25/dropbox-attempts-to-kill-open-source-project/&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;And my article got relegated to the archive. I actually think it's quite neat, so here we go:&lt;/div&gt;
&lt;p&gt;I recently found out something nice about Dropbox: When you put a file in your Dropbox that's already stored somewhere else, you don't have to upload it. It'll appear "magically" in your Dropbox folder, and if it's a shared folder will start syncing almost immediately at the other end.
&lt;div&gt;In and of itself, this is nothing very surprising, Dropbox just computes a hash of the file (or probably some segments of that file) and saves itself the upload if it already knows the contents of the file.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;The interesting thing is that this also works with files that aren't in &lt;i&gt;your&lt;/i&gt; Dropbox, but in any Dropbox anywhere on the world. So, you put a file into your Dropbox folder, Dropbox does its hashing, finds the file somewhere else and puts a reference to the file into your Dropbox web folder.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;Now, imagine what happens when you have two computers, both of them connected to Dropbox, and on different networks. You put a file into one of the Dropbox folders, presumably a large one. Dropbox finds the file for you without upload and starts streaming the file to your other computer instantly.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;Interesting.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;What if we could trick one of the Dropbox clients into thinking you had put a file with some specific hash into its folder? Exactly the same thing would happen. The other computer would start streaming that file instantly from Dropbox's magic file cloud &lt;i&gt;without you ever having a copy&lt;/i&gt;.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;Note: I am not the first person to think of this:&lt;/div&gt;
&lt;div&gt;&lt;a href="http://stackoverflow.com/questions/4767505/exploit-dropbox-file-redundancy-check"&gt;http://stackoverflow.com/questions/4767505/exploit-dropbox-file-redundancy-check&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;Now, where do we get the necessary data for those files? As it so happens, we already have a system that is based on exactly that: transmitting file hashes and file chunk hashes, namely BitTorrent sites. What we could do now is to simply take these hashes, fix up a "zombie" Dropbox client that accepts these hashes instead of files and have any of the many torrented files streamed to you instantly via the Dropbox servers.&lt;/div&gt;
&lt;p&gt;
&lt;div&gt;How lucky is it for Dropbox, then, that the two systems don't fit together exactly...&lt;/div&gt;
&lt;div&gt;&lt;a href="http://stackoverflow.com/questions/1903416/do-any-common-os-file-systems-use-hashes-to-avoid-storing-the-same-content-data-m"&gt;http://stackoverflow.com/questions/1903416/do-any-common-os-file-systems-use-hashes-to-avoid-storing-the-same-content-data-m&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;</description><pubDate>Tue, 24 May 2011 20:15:24 +0200</pubDate></item><item><title>Readable / TastefulWords.com</title><link>https://shezi.de//posts/2011-05-09-readable-tastefulwordscom.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;
&lt;p&gt;Whenever you're on an &lt;strong&gt;article page&lt;/strong&gt;, no matter the website, just click the &lt;strong&gt;Readable&lt;/strong&gt; bookmarklet; a reformatted version of the article will appear on top of the original page.&lt;/p&gt;
&lt;p&gt;"What's an article page?", you ask; good question. An &lt;strong&gt;article page&lt;/strong&gt; is any page that contains one large block of text — like, for example, all newspaper articles. Readable is only designed to automatically process these kinds of pages.&lt;/p&gt;
&lt;p&gt;On any other type of page, you can still take advantage of Readable, but in &lt;strong&gt;manual mode&lt;/strong&gt;: simply &lt;strong&gt;select the text&lt;/strong&gt; you would like Readable to display, and &lt;strong&gt;then click&lt;/strong&gt; the bookmarklet.&lt;/p&gt;
&lt;p&gt;When you're done reading, &lt;strong&gt;double-click the background&lt;/strong&gt; to dismiss the Readable overlay.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://readable.tastefulwords.com/"&gt;readable.tastefulwords.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Readable -- what Readability was before it became something else.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Mon, 09 May 2011 16:51:00 +0200</pubDate></item><item><title>The better iTunes</title><link>https://shezi.de//posts/2011-05-01-the-better-itunes.html</link><description>&lt;p&gt;I recently discovered Clementine ( &lt;a href="http://www.clementine-player.org/"&gt;http://www.clementine-player.org/&lt;/a&gt; ). It's what Amarok and iTunes started out as and what they should still be: music players. It organizes your collection, copies files where you want them, lets you search and catalog your music and it plays it. And it's cross-platform.
&lt;p&gt; Nothing more, nothing less.
&lt;p&gt; And bonus points for Fredrika Stahl on the front page.&lt;/p&gt;&lt;/p&gt;</description><pubDate>Sun, 01 May 2011 10:20:39 +0200</pubDate></item><item><title>The T-Mobile Royal Wedding</title><link>https://shezi.de//posts/2011-05-01-the-t-mobile-royal-wedding.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.youtube.com/watch?v=Kav0FEhtLug&amp;amp;feature=player_embedded"&gt;youtube.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;It, really, IS the better royal wedding!&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 01 May 2011 10:17:00 +0200</pubDate></item><item><title>Shapeways | blog: Theo Jansen's 3D Printed Strandbeests</title><link>https://shezi.de//posts/2011-04-30-shapeways-blog-theo-jansens-3d-printed-strand.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;      [youtube http://www.youtube.com/watch?v=XjDktMn3gA4&amp;fs=1&amp;hl=nl_NL]
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.shapeways.com/blog/archives/822-Theo-Jansens-3D-Printed-Strandbeests.html"&gt;shapeways.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Finally, someone is putting those 3D-printers to good use...&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sat, 30 Apr 2011 12:18:00 +0200</pubDate></item><item><title>WTF! Insane Human Curling by BIC Flex 3</title><link>https://shezi.de//posts/2011-04-17-wtf-insane-human-curling-by-bic-flex-3.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.youtube.com/watch?v=FlCVE0OG-tI"&gt;youtube.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;What the heck?&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 17 Apr 2011 13:58:00 +0200</pubDate></item><item><title>the walrus and the oyster: Life Lessons</title><link>https://shezi.de//posts/2011-04-11-the-walrus-and-the-oyster-life-lessons.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;      &lt;a href="http://www.thewalrusandtheoyster.com/2011/04/life-lessons.html"&gt;http://www.thewalrusandtheoyster.com/2011/04/life-lessons.html&lt;/a&gt;
&lt;p&gt;Six very important life lessons.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Mon, 11 Apr 2011 18:07:00 +0200</pubDate></item><item><title>Access Main Computer File</title><link>https://shezi.de//posts/2011-04-11-access-main-computer-file.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;img alt="Media_http29mediatumb_vtdrg" height="281" src="{{ site.baseurl }}/assets/media_http29mediatumb_vtdrg-scaled500.jpg" width="500" /&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://accessmaincomputerfile.net/"&gt;accessmaincomputerfile.net&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;History of computer GUIs in film.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Mon, 11 Apr 2011 17:57:00 +0200</pubDate></item><item><title>Tune for Two (2011) on Vimeo</title><link>https://shezi.de//posts/2011-04-11-tune-for-two-2011-on-vimeo.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;      [vimeo http://www.vimeo.com/21362582 w=500&amp;h=283]
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://vimeo.com/21362582"&gt;vimeo.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;These are dark times, when you need to have a little fun!&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Mon, 11 Apr 2011 17:51:00 +0200</pubDate></item><item><title>Language of the month: Dead in the Water</title><link>https://shezi.de//posts/2011-03-26-47443968.html</link><description>&lt;p&gt;&lt;span style="font-size:small;"&gt;I was just reading an article called "&lt;span style="font-family:Arial, Helvetica, Verdana, sans-serif;font-weight:bold;line-height:21px;"&gt;Language of the Month: Mirah"&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size:small;"&gt;&amp;nbsp;about a programming language called 'Mirah' on the JVM. It's a really nicely written article, the language seems good enough and all the points in the article are well thought-out and well articulated.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size:small;"&gt;However, I stopped reading when I came to this bit:&lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;div class="CodeRay"&gt;
&lt;div class="code"&gt;
&lt;pre&gt;puts &amp;quot;Hello, world!&amp;quot;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style="clear:both;font-size:.8em;font-family:Verdana, Arial, Helvetica, sans-serif;padding-left:30px;"&gt;&lt;span style="font-size:small;"&gt;We have our first program in Mirah. Let's run it with the&amp;nbsp;&lt;strong&gt;mirah&lt;/strong&gt;command:&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style="clear:both;"&gt;&lt;span style="font-size:small;"&gt;&lt;span style="font-family:Verdana, Arial, Helvetica, sans-serif;"&gt;(from &lt;a href="http://drdobbs.com/java/229400307#"&gt;http://drdobbs.com/java/229400307#&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="clear:both;"&gt;&lt;span style="font-size:small;"&gt;Mirah will never catch on, and you can see that fact from this simple line. And half of the intended and clearly-stated target audience already browsed on. Why? Because it puts, instead of printing. It looks and sounds as if that is such a simple thing, but it's actually something a lot deeper.&lt;/span&gt;&lt;/p&gt;
&lt;p style="clear:both;"&gt;&lt;span style="font-size:small;"&gt;No one cares about the difference between puts and prints when you want to know how to output something to the screen. In most of the software we write, that never happens anyway, because we have graphical interfaces, and HTTP, and logging facades. However, the language designers chose to use a &lt;em&gt;different&lt;/em&gt; keyword for one of the most basic interactions with the language than what most users, coming from Java, would be used to, or would guess on the first try. They probably had very good reasons to do so, but it just turns away so many people trying out your new language and it probably prevents you from reaching critical mass.&lt;/span&gt;&lt;/p&gt;
&lt;p style="clear:both;"&gt;&lt;span style="font-size:small;"&gt;Also, this little fact shows that you care more about the functional background and history of your language than about mass-scale adoption. And in fact, the next highlight is only a few lines away:&lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;div class="CodeRay"&gt;
&lt;div class="code"&gt;
&lt;pre&gt;public static String hello(String name) {  return &amp;quot;Hello, &amp;quot; + name + &amp;quot;!&amp;quot;;}&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="CodeRay"&gt;
&lt;div class="code"&gt;
&lt;pre&gt;def hello(name:String)  &amp;quot;Hello, #{name}!&amp;quot;end&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;p style="clear:both;"&gt;&lt;span style="font-size:small;"&gt;Ok, the syntax doesn't look too bad, to my exotic-language-trained eyes. But to a Java programmer? Why does it not state the return type? Why are name and type switched around in the parameter list? And where the heck do they define their blocks? Mixed indentation and end-token syntax? What? Why? The next 30% of your audience is gone at this point.&lt;/span&gt;&lt;/p&gt;
&lt;p style="clear:both;"&gt;&lt;span style="font-size:small;"&gt;It goes on and on and on:&lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;div class="CodeRay"&gt;
&lt;div class="code"&gt;
&lt;pre&gt;def run    frame = JFrame.new @title    frame.setSize @width, @height    button = JButton.new &amp;quot;Press me&amp;quot;    frame.add button    button.addActionListener do |event|      JButton(event.getSource).setText &amp;quot;Mirah rocks!&amp;quot;    end    frame.setVisible true  end&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;p style="clear:both;"&gt;&lt;span style="font-size:small;"&gt;Yeah, I see that that's Ruby. But why not use Ruby, then? It's not even worth going through the rest of the article, because there's only a fraction of the audience still remaining, and those are nodding their heads, saying to themselves: "Looks nice, like Ruby. Oh, I have to try Ruby again sometime." It's really a pity, because the language looks nice enough and the productivity you'd get out of it is probably five times that of Java.&lt;/span&gt;&lt;/p&gt;
&lt;p style="clear:both;"&gt;&lt;span style="font-size:small;"&gt;But, Mirah is never going to be a mainstream language. Because it puts out.&lt;/span&gt;&lt;/p&gt;
&lt;p style="clear:both;"&gt;&lt;span style="font-size:small;"&gt;(Yeah, I saved a great pun for last...)&lt;/span&gt;&lt;/p&gt;</description><pubDate>Sat, 26 Mar 2011 19:56:00 +0100</pubDate></item><item><title>MicroISV on a Shoestring: B2C stands for "Bingo To Customer"</title><link>https://shezi.de//posts/2011-03-26-microisv-on-a-shoestring-b2c-stands-for-bingo.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;      &amp;nbsp;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.kalzumeus.com/"&gt;kalzumeus.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Wonderful, and so true. Makes me want to try a million different things right now.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sat, 26 Mar 2011 19:09:00 +0100</pubDate></item><item><title>Mercurial RSS</title><link>https://shezi.de//posts/2011-03-20-mercurial-rss.html</link><description>&lt;p&gt;The RSS feed that is built into Mercurial is a great thing. It let's you track easily who's checking in and what get's done in your repositories.&lt;/p&gt;
&lt;p&gt;The preconfigured layout, however, is a bit spartan. It only shows the date, the commit message and the author. This always bugged me, because I'd like to see how much was done and how many files were changed on each commit.&lt;/p&gt;
&lt;p&gt;Luckily, the contents of the RSS feed get prepared through a template engine. This makes it possible to change the contents of the feed without needing to muck around with too much stuff.&lt;/p&gt;
&lt;p&gt;The following repeats much of the information you can also find on &lt;a href="http://mercurial.selenic.com/wiki/Theming"&gt;http://mercurial.selenic.com/wiki/Theming&lt;/a&gt; but is specific to RSS feed customization.&lt;/p&gt;
&lt;p&gt;If you're on Ubuntu, the files we're going to look at are stored in&lt;/p&gt;
&lt;div class="CodeRay"&gt;
&lt;div class="code"&gt;
&lt;pre&gt;/usr/share/mercurial/templates/rss/&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The feeds use the same framework as the web-served styles, which is why the layout and the file structures are a bit confusing at first. Basically, the RSS feed is composed of three files: the header (header.tmpl), the actual content (changelog.tmpl), within which single items (changelogentry.tmpl) are looped, and the footer (footer.htmpl). Since RSS feeds don't offer much in terms of customizability, we can focus on each individual entry, stored in changelogentry.tmpl. Looking at that give us this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt; &amp;lt;item&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title&amp;gt;{desc|strip|firstline|strip|escape}&amp;lt;/title&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;guid isPermaLink="true"&amp;gt;{urlbase}{url}rev/{node|short}&amp;lt;/guid&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;description&amp;gt;&amp;lt;![CDATA[{desc|strip|escape|addbreaks|nonempty}]]&amp;gt;&amp;lt;/description&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;author&amp;gt;{author|obfuscate}&amp;lt;/author&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;pubDate&amp;gt;{date|rfc822date}&amp;lt;/pubDate&amp;gt; &amp;lt;/item&amp;gt; &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Looks nice. But I don't really need the description twice. So let's change the contents of &amp;lt;description&amp;gt; (which is what get's displayed in the RSS reader as the contents) with a list of changed files. For that, we simply exchange the description line, so that the file looks like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt; &amp;lt;item&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title&amp;gt;{desc|strip|firstline|strip|escape}&amp;lt;/title&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;guid isPermaLink="true"&amp;gt;{urlbase}{url}rev/{node|short}&amp;lt;/guid&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;description&amp;gt;&amp;lt;![CDATA[{files}]]&amp;gt;&amp;lt;/description&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;author&amp;gt;{author|obfuscate}&amp;lt;/author&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;pubDate&amp;gt;{date|rfc822date}&amp;lt;/pubDate&amp;gt; &amp;lt;/item&amp;gt; &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Trying this, however, results an error. The reason for this is because the template engine will give us a "filedifflink" for each file, i.e. we have to prepare a template fragment for the filedifflink. The template fragments are stored in the file ''map'' and to define the fragment, we simply add this line to the file:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;  filedifflink = '&lt;a href="{urlbase}{url}diff/{node|short}/{file|urlescape}"&gt;{file|escape}&lt;/a&gt;&amp;lt;br/&amp;gt;' &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And that's all. Now the RSS feed contains for each entry a list of files that were changed, complete with a link to the file changes on the web frontend.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description><pubDate>Sun, 20 Mar 2011 18:59:00 +0100</pubDate></item><item><title>Muffin - Wikipedia, the free encyclopedia</title><link>https://shezi.de//posts/2011-03-19-muffin-wikipedia-the-free-encyclopedia.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote class="posterous_long_quote"&gt;The corn muffin is the official state muffin of &lt;a href="http://en.wikipedia.org/wiki/Massachusetts"&gt;Massachusetts&lt;/a&gt;.&lt;sup class="reference"&gt;&lt;a href="http://en.wikipedia.org/wiki/Muffin#cite_note-4"&gt;&lt;span&gt;[&lt;/span&gt;5&lt;span&gt;]&lt;/span&gt;&lt;/a&gt;&lt;/sup&gt;&lt;br /&gt;The &lt;a href="http://en.wikipedia.org/wiki/Blueberry_muffin"&gt;blueberry muffin&lt;/a&gt; is the official state muffin of &lt;a href="http://en.wikipedia.org/wiki/Minnesota"&gt;Minnesota&lt;/a&gt;.&lt;sup class="reference"&gt;&lt;a href="http://en.wikipedia.org/wiki/Muffin#cite_note-5"&gt;&lt;span&gt;[&lt;/span&gt;6&lt;span&gt;]&lt;/span&gt;&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://en.wikipedia.org/wiki/Muffin"&gt;en.wikipedia.org&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;And I didn't even know states had an "official muffin".&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sat, 19 Mar 2011 12:22:00 +0100</pubDate></item><item><title>OK Go - End Love</title><link>https://shezi.de//posts/2011-03-19-ok-go-end-love.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.youtube.com/watch?v=kjIiW3mZrT4"&gt;youtube.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Wonderful new video by OK go.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sat, 19 Mar 2011 11:43:00 +0100</pubDate></item><item><title>Germany's new boom: making money by making stuff | World news | The Guardian</title><link>https://shezi.de//posts/2011-03-16-germanys-new-boom-making-money-by-making-stuf.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;img alt="Media_httpstaticguimc_vpbqo" height="276" src="{{ site.baseurl }}/assets/media_httpstaticguimc_vpbqo-scaled500.jpg" width="460" /&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.guardian.co.uk/world/2011/mar/14/germany-new-boom-making-stuff"&gt;guardian.co.uk&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Looks like good old Europe &lt;i&gt;did&lt;/i&gt; do something right after all. Amazing!&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 16 Mar 2011 15:36:00 +0100</pubDate></item><item><title>Frenzy - The Dropbox powered social network</title><link>https://shezi.de//posts/2011-03-16-frenzy-the-dropbox-powered-social-network.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/03/media_httpfrenzyappco_unpgk-scaled1000.jpg"&gt;&lt;img alt="Media_httpfrenzyappco_unpgk" height="238" src="{{ site.baseurl }}/assets/media_httpfrenzyappco_unpgk-scaled1000.jpg?w=300" width="500" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://frenzyapp.com/"&gt;frenzyapp.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;That is one brilliant idea! I love it, and I will try it out.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 16 Mar 2011 14:07:00 +0100</pubDate></item><item><title>Lego Carcassonne</title><link>https://shezi.de//posts/2011-03-07-lego-carcassonne.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/03/media_httpwwwiamcalco_ndplj-scaled1000.png"&gt;&lt;img alt="Media_httpwwwiamcalco_ndplj" height="375" src="{{ site.baseurl }}/assets/media_httpwwwiamcalco_ndplj-scaled1000.png?w=300" width="500" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.iamcal.com/lego-carcassonne/"&gt;iamcal.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Clever trick and nice build. You could extend it to a building session, where everyone can build up real cities into their parts.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Mon, 07 Mar 2011 11:28:00 +0100</pubDate></item><item><title>The Impersonal Non-Computer</title><link>https://shezi.de//posts/2011-03-06-the-impersonal-non-computer.html</link><description>&lt;p&gt;A few days ago, the iPad 2 was unveiled by Steve Jobs, and already people are gushing over how much better the world is going to be now that we have a faster iPad.&lt;/p&gt;
&lt;p&gt;One piece I have read with much interest is &lt;a href="http://daringfireball.net/2011/03/the_chair"&gt;Daring Fireballs "The Chair"&lt;/a&gt;. It is well written, not too enthusiastic and he makes a few good points. However, there is something I have a lot of problems with, and it's the last sentence:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s a shame, almost, that we squandered the term “personal computer” 30 years ago.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, while this is a very good ending sentence for the article, I disagree with almost every point of the sentence. First off is the implication that the term "personal computer" was squandered on the distinction between mainframes, where you timeshared the computer and single-user computers. This distinction was a very important one, as it started the trend towards each and everyone having one or more computers, as well as towards miniaturization, as opposed to room-filling mainframe servers. In this way, computers have become more and more personal over the years, indeed, going so far that I can now buy fully assembled and working computers with a complete selection of software installed to my tastes in almost any electronics store. In fact, the computers that I am working on have exactly the software and exactly the settings I need and I want, sometimes frustrating my friends and coworkers to the extent that they'd rather user their own computer than mine.
I suppose that the term "personal computer" is very fitting for that device, and I don't think the name was "squandered" on that kind of machine.&lt;/p&gt;
&lt;p&gt;But the second gripe I have is with the implication that the iPad should actually be a personal computer at all. Apart from the fact that it doesn't fit the generally accepted cluster of things we call "PC"s very well, it is not very personal at all. All the iPads I have seen have had the same look, the same background picture, the same interaction modes, almost the same software installed and were used for almost exactly the same things (namely, Angry Birds and Fruit Ninja). Now, that is not a bad thing, and actually, what most people want and need are monocultures and pre-chewed content, as is apparent from the success that mainstream tv and music have had, and that Apple now has. But it can't really be called "personal" in any meaningful way, not much more than the big tv stations can be called "personal" for reading out twitter messages.&lt;/p&gt;
&lt;p&gt;And finally, for me and for the most people, the iPad is not actually a computer. For me, having a computer science background, computers are inherently programmable devices. And I cannot program my iPad in any interesting way. Oh, sure, you say, I could spend a hundred dollars a year for the developer license, another 1500 on a Macbook and then the 500 for the iPad itself, and for a mere 2000 dollars I can get a programmable computer, for some time. But you know what? For 2000 dollars, I can get together with a few of my friends and let them do computations for me. I wouldn't call a room full of my friends doing computations a computer, even though they're a lot more personal to me than the iPad is.&lt;/p&gt;
&lt;p&gt;Now, I covet Apple products as much as the next guy, and the iPad is a beautiful device and I'd like everyone to have one. I just don't think we should epitomize it as the One True Computer. And while iPads are bringing computing devices to more and more people, we should certainly not compare the introduction of a toy to the massive, worldwide revolution that was brought on by personal computing.&lt;/p&gt;</description><pubDate>Sun, 06 Mar 2011 10:56:00 +0100</pubDate></item><item><title>Jenga Driven Development</title><link>https://shezi.de//posts/2011-03-02-jenga-driven-development.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;img alt="Media_httpprogramming_zdjld" height="500" src="{{ site.baseurl }}/assets/media_httpprogramming_zdjld-scaled500.jpg" width="335" /&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://programmingzen.com/2011/03/01/jenga-driven-development/"&gt;programmingzen.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I have worked with legacy projects and the picture explains exactly how it feels...&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 02 Mar 2011 15:05:00 +0100</pubDate></item><item><title>Trent Walton</title><link>https://shezi.de//posts/2011-02-20-trent-walton.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;Check out this website I found at &lt;a href="http://trentwalton.com/"&gt;trentwalton.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Very well worth the read, as it hits exactly what I feel about how work should be. If you're not into the piece of work you're doing right now, you should complete it and then find something that offsets what you missed that time.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 20 Feb 2011 10:41:00 +0100</pubDate></item><item><title>The Best Questions For A First Date « OkTrends</title><link>https://shezi.de//posts/2011-02-20-the-best-questions-for-a-first-date-oktrends.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote class="posterous_long_quote"&gt;
&lt;h4 class="wanna_know"&gt;Okay, if you want to know...&lt;/h4&gt;
&lt;h2 class="wanna_know"&gt;Will my date have sex on the first date?&lt;/h2&gt;
&lt;h4 class="should_ask"&gt;Ask...&lt;/h4&gt;
&lt;ul class="footnote" style="font-size:16px;padding:20px;font-family:georgia;color:#221;background:#ff3;"&gt;
&lt;li&gt;Do you like the taste of beer?&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://blog.okcupid.com/index.php/the-best-questions-for-first-dates/"&gt;blog.okcupid.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Hilarious statistical correlation analysis of the best questions to ask on a first date and the deeper issues you can find out about. However, not very relevant for us Europeans, because all of the questions asked have very different contexts in Europe -- especially the beer question.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 20 Feb 2011 09:40:00 +0100</pubDate></item><item><title>Flight of the Bumblebee on 101 Bottles!</title><link>https://shezi.de//posts/2011-02-18-flight-of-the-bumblebee-on-101-bottles.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.youtube.com/watch?v=WzucpFgi7Xk&amp;amp;feature=player_embedded"&gt;youtube.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Wonderful idea for after big parties.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Fri, 18 Feb 2011 22:43:00 +0100</pubDate></item><item><title>Mark Hughes's answer to Given our current technology and with the proper training, would it be possible for someone to become Batman? - Quora</title><link>https://shezi.de//posts/2011-02-18-mark-hughess-answer-to-given-our-current-tech.html</link><description>&lt;blockquote&gt;&lt;p&gt;Anyway, there you are, rooftop to rooftop, and it occurs to you that the cop cars are so far gone now that you barely hear the sirens. So you think "Hmm, no wonder the real Batman has a car, this rooftop thing looks cool but I'll never make it in time to stop a crime that isn't happening within a block or two.&lt;/p&gt;&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.quora.com/Given-our-current-technology-and-with-the-proper-training-would-it-be-possible-for-someone-to-become-Batman/answer/Mark-Hughes-1"&gt;quora.com&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;
Wonderful and eloquent analysis of how you are going to be Batman.
&lt;/p&gt;
&lt;p&gt;
Even though it totally kills the fantasy...
&lt;/p&gt;</description><pubDate>Fri, 18 Feb 2011 19:28:00 +0100</pubDate></item><item><title>journal.stuffwithstuff.com » Blog Archive » Extending Syntax from Within a Language</title><link>https://shezi.de//posts/2011-02-14-journalstuffwithstuffcom-blog-archive-extendi.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote class="posterous_long_quote"&gt;
&lt;p&gt;A &lt;a href="http://magpie.stuffwithstuff.com/internals/the-heart-of-magpie.html"&gt;big goal&lt;/a&gt; with my little language Magpie is to do as much as possible at the library level and keep the core language small. I’ve been toiling for the past few weeks and I finally moved a &lt;em&gt;huge&lt;/em&gt; chunk of Magpie over to the library side: &lt;em&gt;all infix operators are now defined and implemented in Magpie&lt;/em&gt;. As far as the core language is concerned, it doesn’t even &lt;em&gt;have&lt;/em&gt; operators.&lt;/p&gt;
&lt;p&gt;Not only can you now define your own infix operators, including specifying precedence and associativity, but other large chunks of Magpie syntax are now defined at the library level.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://journal.stuffwithstuff.com/2011/02/13/extending-syntax-from-within-a-language/"&gt;journal.stuffwithstuff.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Wonderful, simply wonderful. I encourage you to read the whole article and I encourage myself to try Magpie soon...&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Mon, 14 Feb 2011 19:29:00 +0100</pubDate></item><item><title>How to Get a Raise | SebastianMarshall.com: Strategy, Philosophy, Self-Discipline, Science. Victory.</title><link>https://shezi.de//posts/2011-02-13-how-to-get-a-raise-sebastianmarshallcom-strat.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote class="posterous_long_quote"&gt;
&lt;p&gt;The question was, “Why do business analysts and project managers get higher salaries than programmers?”&lt;/p&gt;
&lt;p&gt;My reply -&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Because programmers don’t ask for more money often enough.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.sebastianmarshall.com/how-to-get-a-raise"&gt;sebastianmarshall.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;He's right. Not all of what he says afterwards is completely true for salaried employees everywhere, but the basic idea is correct.&lt;/p&gt;
&lt;p&gt;What I find fascinating as well is that for a business analyst or a customer-facing consultant, asking for more money is part of the job. So they get a lot of training talking to people and asking for more, while the technical, background-loving people don't.&lt;/p&gt;
&lt;p&gt;So, let's all start practising and asking for raises. If you don't get one -- no harm done, you can always go on doing what you already do...&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 13 Feb 2011 21:35:00 +0100</pubDate></item><item><title>Graham Wetzler: Why Dropbox Succeeded and Syncplicity Didn’t</title><link>https://shezi.de//posts/2011-02-02-graham-wetzler-why-dropbox-succeeded-and-sync.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;Check out this website I found at &lt;a href="http://wetzler.me/dropbox-syncplicity/"&gt;wetzler.me&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;It's a strange thing, noting that some service was "better" because it has less features. I have to try that sometimes.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 02 Feb 2011 20:21:00 +0100</pubDate></item><item><title>ReMarkdown: HTML as Markdown text using CSS</title><link>https://shezi.de//posts/2011-01-27-remarkdown-html-as-markdown-text-using-css.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;Check out this website I found at &lt;a href="http://covertprestige.info/css/remarkdown/"&gt;covertprestige.info&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Remarkdown -- lets you convert HTML to Markdown. Nice hack.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Thu, 27 Jan 2011 12:34:00 +0100</pubDate></item><item><title>while true blog » What if Visual Studio had Achievements?</title><link>https://shezi.de//posts/2011-01-26-while-true-blog-what-if-visual-studio-had-ach.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;
&lt;p&gt;What if Visual Studio supported achievements, just like games on Steam, Xbox or PS3? Bragging to your coworkers about which one you’ve just unlocked, imagine that! Here’s a little proposed list for some of them. .NET / C# flavored, of course.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Falling Down&lt;/b&gt; – Created a new SharePoint project&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Job Security&lt;/b&gt; – Written a LINQ query with over 30 lines of code&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Sword Fighter&lt;/b&gt; – 5 Consecutive Solution Rebuilds with zero code changes&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Shotgun Debugging&lt;/b&gt; –  5 Consecutive Solution Rebuilds with a single character change&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Mathematician&lt;/b&gt; – Defined 15 local variables with a single character name&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Academic&lt;/b&gt; – Written 1000 lines of F#&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Spaghetti Monster&lt;/b&gt; – Written a single line with more than 300 characters&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Wild One&lt;/b&gt; – Mixed tabs and spaces for indentation more than 5 times in a single line&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Organizer&lt;/b&gt; – Created a Solution with more than 50 projects&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Portal&lt;/b&gt; – Created a circular project dependency&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Multitasker&lt;/b&gt; – Have more than 50 source files open at the same time&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Code Keeper&lt;/b&gt; – Uninstalled Resharper because it made you redundant&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pasta Chef&lt;/b&gt; – Created a class with more than 100 fields, properties or methods&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Procedural Programmer&lt;/b&gt; – Created a method with more than 10 out parameters&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Steam Powered&lt;/b&gt; – Added Visual Studio as a Steam game&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Poet&lt;/b&gt; – Written a source file with more than 10,000 lines&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Enterprise&lt;/b&gt; – Build Solution took more than 10 minutes&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Highway to Hell&lt;/b&gt; – Successfully created a WCF service&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Explainer&lt;/b&gt; – Written a comment with more than 100 words&lt;/li&gt;
&lt;li&gt;&lt;b&gt;TPS Reports&lt;/b&gt; – Created a Crystal Reports Project&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Rage Quit&lt;/b&gt; – ALT+F4 after a failed bug fix&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ooooh Shiny&lt;/b&gt; – Written 100 extensions methods&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Look Ma&lt;/b&gt; – Written an infinite Fibonacci generator using yield&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Engineer&lt;/b&gt; – Killed a zombie with The Wrench&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Architect&lt;/b&gt; – Created 25 Interfaces in a single project&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Right Way&lt;/b&gt; – Test method is longer than the tested method&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Defender&lt;/b&gt; – Checked every argument for null exceptions&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pokemon Programming&lt;/b&gt; – Caught all the exceptions&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Black Magic&lt;/b&gt; – Implemented a RealProxy&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Gimme back my ASM&lt;/b&gt; – Used ILGenerator&lt;/li&gt;
&lt;li&gt;&lt;b&gt;I’m Sorry&lt;/b&gt; – Created a new Visual Basic Project&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The SEO Expert&lt;/b&gt; – ASP.NET MVC Routing table with more than 100 routes&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Matrix&lt;/b&gt; – Windows Forms with more than 100 controls&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Daredevil&lt;/b&gt; – UpdatePanels nested more than 3 layers deep&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Just a Test&lt;/b&gt; – Nested multiline C-style comments that caused a compilation error&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Warm Bath&lt;/b&gt; – Successfully consumed a non .NET SOAP web service&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Old School&lt;/b&gt; – Defined more than 100 static objects&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Cloner&lt;/b&gt; – Copy-pasted more than 50 lines&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Dependency&lt;/b&gt; – Referenced more than 30 projects&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Paying the bills&lt;/b&gt; – Imported a Visual Basic project&lt;/li&gt;
&lt;li&gt;&lt;b&gt;First Hit&lt;/b&gt; – Included a Codeproject.com library into your project and it actually compiled&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Paula&lt;/b&gt; – Define a firstname field with value &lt;i&gt;Brillant&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Every Option Considered&lt;/b&gt; – Created an enum with more than 30 values&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Inspired by Steam Holiday sales and Battlefield Bad Company 2. Odd web coding exposed on the most &lt;a href="http://www.whiletrue.com/"&gt;minimalistic company page&lt;/a&gt; possible. Enjoy.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://blog.whiletrue.com/2011/01/what-if-visual-studio-had-achievements/"&gt;blog.whiletrue.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Good question. And how can we make it happen?&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 26 Jan 2011 21:14:00 +0100</pubDate></item><item><title>ILUVUXDESIGN on Vimeo</title><link>https://shezi.de//posts/2011-01-26-iluvuxdesign-on-vimeo.html</link><description>&lt;iframe src="//player.vimeo.com/video/19131028" width="1000" height="566" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen&gt;&lt;/iframe&gt;

&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://vimeo.com/19131028"&gt;vimeo.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The UX Designer -- a mythical creature...&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 26 Jan 2011 21:12:00 +0100</pubDate></item><item><title>Saturn's hexagon recreated in the laboratory - The Planetary Society Blog | The Planetary Society</title><link>https://shezi.de//posts/2011-01-26-saturns-hexagon-recreated-in-the-laboratory-t.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.planetary.org/blog/article/00002471/"&gt;planetary.org&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The Saturn Hexagon deciphered.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 26 Jan 2011 21:12:00 +0100</pubDate></item><item><title>Development Chaos Theory » Blog Archive » How (not) to write Factorial in Java.</title><link>https://shezi.de//posts/2011-01-26-development-chaos-theory-blog-archive-how-not.html</link><description>&lt;blockquote&gt;

  &lt;p&gt;I will admit this post was inspired by &lt;a href="http://www.theserverside.com/discussions/thread.tss?thread_id=61725"&gt;How would you write factorial(n) in java?&lt;/a&gt; So excuse me while I rant in code: I have a point to make at the bottom of this article.&lt;/p&gt;
&lt;p&gt;Now most people who write factorial in Java may start with something simple, like:&lt;/p&gt;

  &lt;pre&gt;
public static int factorial(int n) {
    if (n == 0) return 1;
    return n * factorial(n-1);
}&lt;/pre&gt;

&lt;p&gt;Wrapping this in a class (since we’re talking about Java, of course), they may write it inside a utility class. For example:&lt;/p&gt;
&lt;pre&gt;
public class FactorialUtil {
    public static int factorial(int n) {
        if (n == 0) return 1;
        return n * factorial(n-1);
    }
}&lt;/pre&gt;

&lt;p&gt;Simple, no?&lt;/p&gt;
&lt;p&gt;There is a non-recursive solution as well:&lt;/p&gt;

&lt;pre&gt;
public class FactorialUtil {
    public static int factorial(int n) {
        int ret = 1;
        for (int i = 1; i &amp;lt;= n; ++i) ret *= i;
        return ret;
    }
}&lt;/pre&gt;

&lt;p&gt;Now the observent reader may note “gosh, it’s entirely possible the value will be longer than an integer”, so they’ll perhaps rewrite in terms of &lt;code&gt;BigInteger&lt;/code&gt; or at least in terms of &lt;code&gt;long,&lt;/code&gt; depending on the requirements of the program. Thus:&lt;/p&gt;
  &lt;p&gt;...&lt;/p&gt;
  &lt;footer&gt;&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://chaosinmotion.com/blog/?p=622"&gt;chaosinmotion.com&lt;/a&gt;&lt;/div&gt;&lt;/footer&gt;
&lt;/blockquote&gt;

&lt;p&gt;Very funny. And, unfortunately, very true.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 26 Jan 2011 21:10:00 +0100</pubDate></item><item><title>How I Screwed Yasser Arafat out of $2mm (and lost $100mm in the process) -- by Altucher Confidential</title><link>https://shezi.de//posts/2011-01-25-how-i-screwed-yasser-arafat-out-of-2mm-and-lo.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;img alt="Media_httpwwwchinadai_ffcgd" height="450" src="{{ site.baseurl }}/assets/media_httpwwwchinadai_ffcgd-scaled500.jpg" width="374" /&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.jamesaltucher.com/2011/01/how-i-screwed-yasser-arafat-out-of-2mm-and-lost-100mm-in-the-process/"&gt;jamesaltucher.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Nice story.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Tue, 25 Jan 2011 08:54:00 +0100</pubDate></item><item><title>iTerm 2</title><link>https://shezi.de//posts/2011-01-23-iterm-2.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div style="font-family:verdana, sans-serif;"&gt;
&lt;h3&gt;&lt;a name="TOC-What-is-iTerm2-"&gt;&lt;/a&gt;&lt;br /&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;What is iTerm2?&lt;/b&gt;
&lt;p&gt;iTerm2 is a replacement for Terminal and the successor to iTerm. It works on Macs with Leopard or Snow Leopard. Its focus is on performance, internationalization, and supporting innovative features that make your life better.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Why Do I Want It?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Compare &lt;a href="http://sites.google.com/site/iterm2home/iterm2-vs-iterm"&gt;iTerm2 to iTerm&lt;/a&gt;, or compare&lt;a href="http://sites.google.com/site/iterm2home/iterm2-vs-terminal-app"&gt; iTerm2 to Terminal.app&lt;/a&gt;. Still unsure? Check out the &lt;a href="http://sites.google.com/site/iterm2home/features"&gt;features&lt;/a&gt;&amp;nbsp;and &lt;a href="http://sites.google.com/site/iterm2home/screenshots"&gt;screenshots&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;How Do I Use It? &lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Try the &lt;a href="http://sites.google.com/site/iterm2home/support"&gt;FAQ&lt;/a&gt;&amp;nbsp;or the &lt;a href="http://sites.google.com/site/iterm2home/documentation"&gt;docs&lt;/a&gt;. Got problems or ideas? Report them in the &lt;a href="http://code.google.com/p/iterm2/issues/entry"&gt;bug tracker&lt;/a&gt;, or take it to the &lt;a href="http://groups.google.com/group/iterm2-discuss"&gt;forum&lt;/a&gt;. Or tweet to @gnachman and I'll do my best to respond.&lt;/p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://sites.google.com/site/iterm2home/"&gt;sites.google.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I just found iTerm, an open source replacement of the standard Mac Terminal.app. Looks great, even though it's still in alpha stage.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 23 Jan 2011 18:09:00 +0100</pubDate></item><item><title>A brief glimpse of Nokia's popularity outside the Western world</title><link>https://shezi.de//posts/2011-01-20-a-brief-glimpse-of-nokias-popularity-outside.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;a href="http://sheziimport.files.wordpress.com/2011/01/media_httpwwwblogcdnc_ldnpo-scaled1000.jpg"&gt;&lt;img alt="Media_httpwwwblogcdnc_ldnpo" height="359" src="{{ site.baseurl }}/assets/media_httpwwwblogcdnc_ldnpo-scaled1000.jpg?w=300" width="500" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://downloadsquad.switched.com/2011/01/20/a-brief-glimpse-of-nokias-popularity-outside-the-western-world/"&gt;downloadsquad.switched.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Another look at our predisposed views, especially regarding telephones. Shown here is the number of chinese mobile phone users visiting different app stores. Note that both the Android and the Windows app store are in front of Apple, and all three together make up about half of the Nokia OVI Store.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Thu, 20 Jan 2011 23:03:00 +0100</pubDate></item><item><title>There needs to be a Twitter Domain Name Service</title><link>https://shezi.de//posts/2011-01-18-there-needs-to-be-a-twitter-domain-name-servi.html</link><description>&lt;p&gt;I recently acquired a domain name in preparation for a product launch shortly. Being the cool and suave marketing expert that I am, two weeks later I have the idea to get the matching twitter account. Alas, the name is taken and, to make matters worse, by some guy whose only tweet every, was "schooll...", two years ago. So, in the end, I'll have to settle for example_com, or perhaps example'com, since example.com is not an allowed twitter name.&amp;nbsp;
&lt;p&gt; What would be really cool, though, would be if Twitter allowed a period in their twitter names - but only for domain name owners. Verification is easy, too, just put&lt;/p&gt;
&lt;div class="tweetdomain"&gt;
&lt;div class="email"&gt;&amp;lt;div class="tweetdomains"&amp;gt;&amp;lt;div class="email"&amp;gt;&lt;a href="mailto:youremail@example.com"&gt;youremail@example.com&lt;/a&gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div class="email"&gt;on your index page, go to twitter.com/tweetdomain and get your password sent to you.
&lt;p&gt; Twitter: wouldn't you love 200 million instantaneous new accounts?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="email"&gt;&lt;span style="font-size:xx-small;"&gt;P.S.: Yes, I know, there are technical challenges, especially when altering a data scheme like proposed here for the names. But hey, no pain, no gain!&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;</description><pubDate>Tue, 18 Jan 2011 22:30:00 +0100</pubDate></item><item><title>The Nokia N9 Blog » Blog Archive » The world’s most revolutionary mobile phone</title><link>https://shezi.de//posts/2011-01-18-the-nokia-n9-blog-blog-archive-the-worlds-mos.html</link><description>&lt;p&gt;Sometimes I get the wonderful opportunity to reevaluate how I think about things. This time it happened about influence:
&lt;p&gt; &lt;a href="http://thenokian9blog.com/2011/01/05/the-worlds-most-revolutionary-mobile-phone/"&gt;http://thenokian9blog.com/2011/01/05/the-worlds-most-revolutionary-mobile-phone/&lt;/a&gt;
&lt;p&gt; Of course I was of the opinion that the iPhone must have been the most influential phone in recent years, because from my perspective, it changed the way we interacted with the internet and with phones in general, which was an important change. Much more important, however, was the change for the millions upon millions of people who, for the first time in history, did have access to the telephone network at all. Which, I guess, is more important than twittering about your latest meal...&lt;/p&gt;&lt;/p&gt;</description><pubDate>Tue, 18 Jan 2011 21:17:39 +0100</pubDate></item><item><title>Things Real People Don't Say About Advertising</title><link>https://shezi.de//posts/2011-01-16-things-real-people-dont-say-about-advertising.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;img alt="Media_httpmediatumblr_hmbbj" height="360" src="{{ site.baseurl }}/assets/media_httpmediatumblr_hmbbj-scaled500.jpg" width="493" /&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://tpdsaa.tumblr.com/"&gt;tpdsaa.tumblr.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Things don't say about advertising: Saying things about advertising, no one else has the guts to.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 16 Jan 2011 20:09:00 +0100</pubDate></item><item><title>lonelysandwich</title><link>https://shezi.de//posts/2011-01-16-lonelysandwich.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3D Without Glasses, With Nightmarish Disfigurement&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We’ve all seen red-cyan (anaglyph) glasses for 3D viewing. And of course, the more current polarization (RealD Cinema) and even the high-tech Liquid Crystal Shutter glasses. &lt;a href="http://www.youtube.com/watch?v=Uef17zOCDb8"&gt;Now, from this guy&lt;/a&gt;, comes the patented Oh My Christ Turn it Off Turn it Off Goddamn It You’re Scaring the Children What is Wrong with Youvision™.&lt;/p&gt;
&lt;p&gt;via&amp;nbsp;&lt;a href="http://blog.neonmargarita.com/post/2753169851/ever-wondered-how-3d-tv-without-glasses-could"&gt;neonmarg&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://lonelysandwich.com/post/2756106707/3d-without-appetite"&gt;lonelysandwich.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Wonderful parody of the recent 3d-craze.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 16 Jan 2011 20:07:00 +0100</pubDate></item><item><title>Andy Swan » “OK, but there are two rules…”</title><link>https://shezi.de//posts/2011-01-16-andy-swan-ok-but-there-are-two-rules.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;
&lt;h3&gt;&lt;a href="http://andyswan.com/blog/2011/01/05/two-rules/" title="Permanent Link: “OK, but there are two rules…”" rel="bookmark"&gt;“OK, but there are two rules…”&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;abbr title="2011-01-05T12:35:42-0500"&gt;January 5, 2011 – 12:35 pm&lt;/abbr&gt; &lt;/p&gt;
&lt;p&gt;Until recently, Makers Mark has had exactly one product on the shelves since its introduction in 1959. &amp;nbsp;Their response when asked why only one product? &amp;nbsp;”This is the best bourbon.” &amp;nbsp;But even consumers who agreed demanded variety…something different every once in a while.&lt;/p&gt;
&lt;p&gt;The Master Distiller agreed, and persuaded the president, Bill&amp;nbsp;Samuels, Jr,. to at least let him TRY to create a new premium product.&lt;/p&gt;
&lt;p&gt;Samuels response:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“OK, but there are two rules: &amp;nbsp;1. You can’t change the&amp;nbsp;recipe. &amp;nbsp; 2. &amp;nbsp;You can’t do anything that anyone else has done before.”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With those two options off the table, the only alternative was to invent something completely new, or do nothing at all. &amp;nbsp;Long story short….the distiller got to thinking, invented an entirely new process at the end of the aging cycle, &lt;a href="http://www.huffingtonpost.com/2010/06/08/makers-mark-46-bourbon-di_n_604108.html#s86637&amp;amp;title=Four%20Roses"&gt;and now they have a new product, Makers Mark 46.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The buzz in the bourbon world was huge. &amp;nbsp;The product &lt;a href="http://cocktails.about.com/od/whiskeyreviews/fr/Makers-Mark-46-Bourbon-Whiskey.htm"&gt;is a hit.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What Bill Samuels did was &lt;strong&gt;force innovation through voluntary restriction&lt;/strong&gt;. &amp;nbsp; He specifically restricted his master distiller from doing THE two things that every other company in the world would have done: &amp;nbsp;Change the recipe slightly or create a premium “single barrel” or longer-aged version.&lt;/p&gt;
&lt;p&gt;By not allowing yourself to use all of the tools at your disposal….by not allowing yourself to do the things that normal people do….by not allowing yourself to compete on the features that every other product has….you can force yourself to innovate,&amp;nbsp;differentiate, and create a unique value proposition.&lt;/p&gt;
&lt;p&gt;Running a real estate website? &amp;nbsp;OK….you’re not allowed to show the asking price or address of any home. &amp;nbsp;Go.&lt;/p&gt;
&lt;p&gt;Starting &amp;nbsp;a pizza place? &amp;nbsp;OK….no tables or chairs allowed.&lt;/p&gt;
&lt;p&gt;New app to compete with runkeeper? &amp;nbsp; No user inputs allowed. &amp;nbsp;None. &amp;nbsp;Just figure out when they are running.&lt;/p&gt;
&lt;p&gt;Kick the chair out from under you and I bet you’ll stand taller than ever before (or at least fail faster and try something else!)&lt;/p&gt;
&lt;p&gt;Force yourself to innovate through voluntary restriction.&lt;/p&gt;
&lt;p&gt;Win.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://andyswan.com/blog/2011/01/05/two-rules/"&gt;andyswan.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;One way to make innovation happen: Add some useful but artificial constraints. Not only does that make the newly-created product special, but it also makes you think about what you're trying to achieve.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 16 Jan 2011 20:04:00 +0100</pubDate></item><item><title>If You Are So Smart, Why Aren't You an Entrepreneur? Returns to Cognitive and Social Ability: Entrepreneurs versus Employees</title><link>https://shezi.de//posts/2011-01-16-if-you-are-so-smart-why-arent-you-an-entrepre.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;Found something interesting at &lt;a href="http://ideas.repec.org/p/iza/izadps/dp3648.html"&gt;http://ideas.repec.org/p/iza/izadps/dp3648.html&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The smarter you are, the more money you earn -- so far not very surprising. The interesting result, however, is that this works better for entrepreneurs than for employees.&lt;/p&gt;
&lt;p&gt;So, in effect, if you are smart, you should be an entrepreneur.&lt;/p&gt;
&lt;p&gt;I wonder how much of that effect can be explained by selection, both self-selection and external selection by bankruptcy.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sun, 16 Jan 2011 19:55:00 +0100</pubDate></item><item><title>NASA - The Frontier Is Everywhere</title><link>https://shezi.de//posts/2011-01-11-nasa-the-frontier-is-everywhere.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.youtube.com/watch?v=oY59wZdCDo0&amp;amp;feature=player_embedded"&gt;youtube.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Wonderful.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Tue, 11 Jan 2011 14:31:00 +0100</pubDate></item><item><title>There Was Once a Woman Who Had Immortal Cells</title><link>https://shezi.de//posts/2010-12-29-there-was-once-a-woman-who-had-immortal-cells.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;img alt="Media_httpwwwtodayifo_bbdft" height="362" src="{{ site.baseurl }}/assets/media_httpwwwtodayifo_bbdft-scaled500.jpg" width="250" /&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://www.todayifoundout.com/index.php/2010/05/there-was-once-a-woman-who-had-immortal-cells/"&gt;todayifoundout.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;A bit on the popular side, but very interesting.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Wed, 29 Dec 2010 15:56:00 +0100</pubDate></item><item><title>Agile Plumbers</title><link>https://shezi.de//posts/2010-12-25-agile-plumbers.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;Check out this website I found at &lt;a href="http://david.ing.name/2010/12/24/agile-plumbers/"&gt;david.ing.name&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Nice. Didn't know there was a special tag for Posterous-content. Anyway, nice satire on how Agile would work in the real world.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sat, 25 Dec 2010 15:30:00 +0100</pubDate></item><item><title>Fast Iterations: How Napoleon Managed the French Army - Managing Metrics</title><link>https://shezi.de//posts/2010-12-11-fast-iterations-how-napoleon-managed-the-fren.html</link><description>&lt;blockquote&gt;
The armies of the late 1700’s would have looked familiar to Julius Caesar. Thousands of men marching down roads in thin columns with hundreds of supply wagons following behind. The wagons were unable to move across the muddy roads and often forced all the troops to stop and camp whenever the weather turned bad. Opposing armies lumbered slowly across the countryside until they eventually met at the field of battle. The soldiers fanned out into lines that were two men deep with one line firing while the other reloaded. The supply wagons were positioned behind the attacking lines and shuttled food and ammunition up to the fighting and the wounded back to be cared for.&lt;br&gt;
The tactics of war also remained unchanged since Roman times. Generals commanded the forces from the back of the battlefield with scouts in the front closely observing enemy movements. The scouts would carry information back to the generals, who would make decisions and send their orders back to the front.
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://managingmetrics.com/how-napoleon-managed-the-french-army"&gt;managingmetrics.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Agile management styles are nothing new. Even Napoleon used them&lt;/p&gt;</description><pubDate>Sat, 11 Dec 2010 15:11:00 +0100</pubDate></item><item><title>Apple engineer re-creates ancient computer with Legos</title><link>https://shezi.de//posts/2010-12-11-apple-engineer-re-creates-ancient-computer-wi.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://news.cnet.com/8301-17852_3-20025264-71.html"&gt;news.cnet.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Wonderful. Really wonderful. And very well-produced video.&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Sat, 11 Dec 2010 15:09:00 +0100</pubDate></item><item><title>Curved White, Architecture</title><link>https://shezi.de//posts/2010-11-29-curved-white-architecture.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;img alt="Media_http24mediatumb_tadxl" height="500" src="{{ site.baseurl }}/assets/media_http24mediatumb_tadxl-scaled500.jpg" width="500" /&gt;&lt;/div&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://curvedwhite.com/post/1728738492/architecture"&gt;curvedwhite.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Beautiful!&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Mon, 29 Nov 2010 19:45:00 +0100</pubDate></item><item><title>Are You a Manager or a Leader? Who Cares! Just Do What’s Needed! — MakingITclear®</title><link>https://shezi.de//posts/2010-11-29-are-you-a-manager-or-a-leader-who-cares-just.html</link><description>&lt;div class="posterous_bookmarklet_entry"&gt;
&lt;blockquote&gt;
&lt;div&gt;
&lt;p&gt;Business literature is full of distinctions that some very smart people make between a manager and a leader:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Management is doing things right; leadership is doing the right things.&amp;rdquo; &amp;ndash; &lt;a href="http://www.amazon.com/dp/1422120651?tag=makingitclear-20&amp;amp;link_code=as2&amp;amp;creativeASIN=1422120651&amp;amp;creative=374929&amp;amp;camp=211189" target="_blank"&gt;Peter Drucker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;&amp;hellip;Leaders are concerned with what things mean to people. Managers are concerned about how things get done.&amp;rdquo; &amp;ndash; &lt;a href="http://www.amazon.com/dp/B0002185TA?tag=makingitclear-20&amp;amp;link_code=as2&amp;amp;creativeASIN=B0002185TA&amp;amp;creative=374929&amp;amp;camp=211189" target="_blank"&gt;Abraham Zaleznik&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;[...]&lt;/p&gt;
&lt;ul&gt;&lt;/ul&gt;
&lt;p&gt;But the bottom line is that the distinction between a manager and a leader is all semantics.&amp;nbsp; No one hires you &lt;span&gt;&amp;nbsp;&lt;/span&gt;with the job title of Manager and then says, &amp;ldquo;Now don&amp;rsquo;t try leading &amp;mdash; that&amp;rsquo;s not in your job description.&amp;rdquo;&amp;nbsp; And no one hires you with the job title of Leader and then says, &amp;ldquo;Just focus on leading &amp;mdash; managing isn&amp;rsquo;t something you should care about.&amp;rdquo;&amp;nbsp; Let&amp;rsquo;s face it: All management jobs have a leadership component, and all leadership jobs have a management component.&amp;nbsp; And frankly, it doesn&amp;rsquo;t really matter what you call it; what matters is that you get the job done.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div class="posterous_quote_citation"&gt;via &lt;a href="http://blog.makingitclear.com/2010/11/29/managerorleader/"&gt;blog.makingitclear.com&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Very true indeed...&lt;/p&gt;
&lt;/div&gt;</description><pubDate>Mon, 29 Nov 2010 19:05:00 +0100</pubDate></item><item><title>American Software: Lovely Video</title><link>https://shezi.de//posts/2010-11-20-american-software-lovely-video.html</link><description>&lt;p&gt;A wonderful love story and very well done.&lt;/p&gt;

&lt;iframe src="//player.vimeo.com/video/15947949" width="1000" height="566" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;(from &lt;a href="https://vimeo.com/15947949"&gt;https://vimeo.com/15947949&lt;/a&gt;)&lt;/p&gt;</description><pubDate>Sat, 20 Nov 2010 13:30:33 +0100</pubDate></item><item><title>Link: A New Era for Design</title><link>https://shezi.de//posts/2010-11-20-link-a-new-era-for-design.html</link><description>&lt;p&gt;&lt;a href="https://www.kickstarter.com/blog/a-new-era-for-design"&gt;
    https://www.kickstarter.com/blog/a-new-era-for-design&lt;/a&gt;
&lt;p&gt; Very interesting essay on the future of design.&lt;/p&gt;</description><pubDate>Sat, 20 Nov 2010 12:35:25 +0100</pubDate></item></channel></rss>