<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/rss-style.xsl" type="text/xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	     xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>code &#8211; Terence Eden’s Blog</title>
	<atom:link href="https://shkspr.mobi/blog/tag/code/feed/" rel="self" type="application/rss+xml" />
	<link>https://shkspr.mobi/blog</link>
	<description>Regular nonsense about tech and its effects 🙃</description>
	<lastBuildDate>Sat, 21 Feb 2026 10:21:21 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://shkspr.mobi/blog/wp-content/uploads/2023/07/cropped-avatar-32x32.jpeg</url>
	<title>code &#8211; Terence Eden’s Blog</title>
	<link>https://shkspr.mobi/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title><![CDATA[Should HTML's code blocks be translated?]]></title>
		<link>https://shkspr.mobi/blog/2026/01/should-htmls-blocks-be-translated/</link>
					<comments>https://shkspr.mobi/blog/2026/01/should-htmls-blocks-be-translated/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 16 Jan 2026 12:34:53 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[languages]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=63046</guid>

					<description><![CDATA[I was recently prompted to test my blog&#039;s layout when rendered in right-to-left text. Running a website through an automatic translator into a language like Arabic or Hebrew will show you any weird little layout glitches which might occur.  But mechanical translation is a bit of an unthinking brute.  In this example, I had a code snippet which contained the word &#34;link&#34;.    Should that word be…]]></description>
										<content:encoded><![CDATA[<p>I was recently prompted to <a href="https://bsky.app/profile/vale.rocks/post/3lxgvpipy4k2q">test my blog's layout when rendered in right-to-left text</a>. Running a website through an automatic translator into a language like Arabic or Hebrew will show you any weird little layout glitches which might occur.</p>

<p>But mechanical translation is a bit of an unthinking brute.  In this example, I had a code snippet which contained the word "link".</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/08/translate.webp" alt="HTML code block, one of the element names is rendered in Arabic." width="1008" height="567" class="aligncenter size-full wp-image-63048">

<p>Should that word be translated? Obviously not! The code isn't valid unless the element name is in English - and it probably doesn't make sense to reverse the text direction.</p>

<p>Luckily, the HTML specification allows authors to mark specific bits of their page as unsuitable for automatic translations. <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/translate">The <code>translate</code> global attribute</a> can be applied to your markup like this:</p>

<pre><code class="language-html">&lt;code translate="no"&gt;
   &amp;amp;lt;link … &amp;amp;gt;
   &amp;amp;lt;meta … &amp;amp;gt;
   &amp;amp;lt;strong&amp;amp;gt;Hello&amp;amp;lt;/strong&amp;amp;gt;
&lt;/code&gt;
</code></pre>

<p>Nothing inside that code block will be translated. Hurrah!</p>

<p>But there are some problems with this approach.</p>

<p>Consider this pseudo-code:</p>

<pre><code class="language-_">// Reverse the polarity of the neutron flow.
$neutron = $atom.flow( direction="backwards" );
</code></pre>

<p>Fairly obviously, the code itself shouldn't be translated. It simply won't run unless the syntax is precisely as written. But what about the comment at the top? It would probably be useful to have that translated, right?</p>

<p>It is possible to mark up different parts of a document to be translatable even if their parent isn't:</p>

<pre><code class="language-html">&lt;code translate="no"&gt;
   &lt;span translate="yes"&gt;// Reverse the polarity of the neutron flow.&lt;/span&gt;
   $neutron = $atom.flow( direction="backwards" );
&lt;/code&gt;
</code></pre>

<p>At least, that's my understanding of <a href="https://html.spec.whatwg.org/multipage/dom.html#attr-translate">the specification</a>.</p>

<p>This brings us on to another complex problem. Consider this code block which might be embedded in a page as an example:</p>

<pre><code class="language-js">// Ensure the age is calculated from the user's birthday
var age = today.date - user.birthday;
</code></pre>

<p>If translated into Chinese, the comment might say:</p>

<pre><code class="language-js">// 确保年龄是根据用户的生日计算的
var age = today.date - user.birthday;
</code></pre>

<p>But is it useful to have variable names be different between comments and the code?</p>

<p>In some contexts yes, in others no!</p>

<p>And that's where we hit the limits of the current crop of machine-translation algorithms. Without a holistic view of the entire page, and a semantic understanding of how previous words relate to subsequent words, there will always be glitches and gotchas like this.</p>

<p>For now, I'm marking my code blocks as non-translatable but letting comments be fully translated. If you have strong opinions about this - please leave a comment!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=63046&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2026/01/should-htmls-blocks-be-translated/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[1KB JS Numbers Station]]></title>
		<link>https://shkspr.mobi/blog/2025/07/1kb-js-numbers-station/</link>
					<comments>https://shkspr.mobi/blog/2025/07/1kb-js-numbers-station/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 20 Jul 2025 11:34:53 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[tts]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=62005</guid>

					<description><![CDATA[Code Golf is the art/science of creating wonderful little demos in an artificially constrained environment. This year the js1024 competition was looking for entries with the theme of &#34;Creepy&#34;.  I am not a serious bit-twiddler. I can&#039;t create JS shaders which produce intricate 3D worlds in a scrap of code. But I can use slightly obscure JavaScript APIs!  There&#039;s something deliciously creepy about…]]></description>
										<content:encoded><![CDATA[<p>Code Golf is the art/science of creating wonderful little demos in an artificially constrained environment. This year the <a href="https://js1024.fun/">js1024 competition</a> was looking for entries with the theme of "Creepy".</p>

<p>I am not a serious bit-twiddler. I can't create JS shaders which produce intricate 3D worlds in a scrap of code. But I <em>can</em> use slightly obscure JavaScript APIs!</p>

<p>There's something deliciously creepy about <a href="https://priyom.org/number-stations">Numbers Stations</a> - the weird radio frequencies which broadcast seemingly random numbers and words. Are they spies communicating? Commands for nuclear missiles? Long range radio propagation tests? Who knows!</p>

<p>So I decided to build one. <a href="https://js1024.fun/demos/2025/24/bar">Play with the demo</a>.</p>

<p>Obviously, even the <a href="https://shkspr.mobi/blog/2020/09/a-floppy-disk-mp3-player-using-a-raspberry-pi/">most extreme opus compression</a> can't fit much audio into 1KB. Luckily, JavaScript has you covered! Most modern browsers have a built-in Text-To-Speech (TTS) API.</p>

<p>Here's the most basic example:</p>

<pre><code class="language-js">m = new SpeechSynthesisUtterance;
m.text = "Hello";
speechSynthesis.speak(m);
</code></pre>

<p>Run that JS and your computer will speak to you!</p>

<p>In order to make it creepy, I played about with the rate (how fast or slow it speaks) and the pitch (how high or low).</p>

<pre><code class="language-js">m.rate=Math.random();
m.pitch=Math.random()*2;
</code></pre>

<p>It worked disturbingly well! High pitched drawls, rumbling gabbling, the languid cadence of a chattering friend. All rather creepy.</p>

<p>But <em>what</em> could I make it say? Getting it to read out numbers is pretty easy - this will generate a random integer:</p>

<pre><code class="language-js">s = Math.ceil( Math.random()*1000 );
</code></pre>

<p>But a list of words would be tricky. There's not much space in 1,024 bytes for anything complex. The rules say I can't use any external resources; so are there any <em>internal</em> sources of words? Yes!</p>

<pre><code class="language-js">Object.getOwnPropertyNames( globalThis );
</code></pre>

<p>That gets all the properties of the global object which are available to the browser! Depending on your browser, that's over 1,000 words!</p>

<p>But there's a slight problem. Many of them are quite "computery" words like "ReferenceError", "URIError", "Float16Array". I wanted all the <em>single</em> words - that is, anything which only has one capital letter and that's at the start.</p>

<pre><code class="language-js">const l = (n) =&gt; {
    return ((n.match(/[A-Z]/g) || []).length === 1 &amp;&amp; (n.charAt(0).match(/[A-Z]/g) || []).length === 1);
};

//   Get a random result from the filter
s = Object.getOwnPropertyNames( globalThis ).filter( l ).sort( ()=&gt;.5-Math.random() )[0]
</code></pre>

<p>Rather pleasingly, that brings back creepy words like "Event", "Atomics", and "Geolocation".</p>

<p>Of course, Numbers Stations don't just broadcast in English.  The TTS system can vocalise in multiple languages.</p>

<pre><code class="language-js">//   Set the language to Russian
m.lang = "ru-RU";
</code></pre>

<p>OK, but where do we get all those language strings from? Again, they're built in and can be retrieved randomly.</p>

<pre><code class="language-js">var e = window.speechSynthesis.getVoices();
m.lang = e[ (Math.random()*e.length) |0 ]
</code></pre>

<p>If you pass the TTS the number 555 and ask it to speak German, it will read out <i lang="de">fünfhundertfünfundfünfzig</i>.</p>

<p>And, if you tell the TTS to speak an English word like "Worker" in a foreign language, it will pronounce it with an accent.</p>

<p>Randomly altering the pitch, speed, and voice to read out numbers and dissociated words produces, I think, a rather creepy effect.</p>

<script>const l = (n) => {
    return ((n.match(/[A-Z]/g) || []).length === 1 && (n.charAt(0).match(/[A-Z]/g) || []).length === 1);
};
m = new SpeechSynthesisUtterance;

function g() {
    setInterval(() => {
        s = Object.getOwnPropertyNames(globalThis).filter(l).sort(() => .5 - Math.random())[0]
        if (Math.random() > .3) {
            s = Math.ceil(Math.random() * 1000);
        }
        var e = window.speechSynthesis.getVoices();
        m.rate = Math.random(), m.pitch = Math.random() * 2, m.text = s, m.lang = e[(Math.random() * e.length) | 0]["lang"];
        speechSynthesis.speak(m);
    }, 2501);
}</script>

<p>If you want to test it out, you can press this button. I find that it works best in browsers with a good TTS engine - let me know how it sounds on your machine.</p>

<p><button onclick="g()">🅝🅤🅜🅑🅔🅡🅢 🅢🅣🅐🅣🅘🅞🅝</button></p>

<p>With the remaining few bytes at my disposal, I produced a quick-and-dirty random pattern using Unicode drawing blocks. It isn't very sophisticated, but it does have a little random animation to it.</p>

<p>You can <a href="https://js1024.fun/demos/2025">play with all the js1024 entries</a> - I would be delighted if you voted <a href="https://js1024.fun/demos/2025/24/bar">for mine</a>.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=62005&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/07/1kb-js-numbers-station/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Are there any modern closed-source programming languages?]]></title>
		<link>https://shkspr.mobi/blog/2023/04/are-there-any-modern-closed-source-programming-languages/</link>
					<comments>https://shkspr.mobi/blog/2023/04/are-there-any-modern-closed-source-programming-languages/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 08 Apr 2023 11:34:25 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[programming]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=45420</guid>

					<description><![CDATA[At a recent OpenUK meetup, one of the participants declared that Open Source had comprehensively won. While businesses might not always release their proprietary source code, 100% of everything they wrote used an open source programming language.  I wondered how true that was. You can, perhaps, moan about the shenanigans around Java&#039;s licencing and you mutter about whether it is easy to get…]]></description>
										<content:encoded><![CDATA[<p>At a recent <a href="https://openuk.uk/">OpenUK</a> meetup, one of the participants declared that Open Source had comprehensively won. While businesses might not always <em>release</em> their proprietary source code, 100% of everything they wrote used an open source programming language.</p>

<p>I wondered how true that was. You can, perhaps, moan about <a href="https://www.bbc.co.uk/news/technology-56639088">the shenanigans around Java's licencing</a> and you mutter about whether it is easy to get involved with C++'s <a href="https://isocpp.org/std/the-committee">JTC1/SC22/WG21</a> - but the core tech behind the two is open. Anyone can read the language specification freely and anyone can build a compiler legally.</p>

<p>It's the same with, to choose a random smattering, <a href="https://github.com/python/cpython">Python</a>, <a href="https://github.com/dotnet/fsharp/">F#</a>, and even <a href="https://github.com/apple/swift">Apple's Swift</a>.</p>

<p>Once upon a time, Flash ruled the roost. Now Flash is dead<sup id="fnref:gordon"><a href="https://shkspr.mobi/blog/2023/04/are-there-any-modern-closed-source-programming-languages/#fn:gordon" class="footnote-ref" title="Flash may be dead but... Clip from the movie &quot;Flash Gordon&quot;. Brian Blessed's character says &quot;Gordon's Alive!?&quot;" role="doc-noteref">0</a></sup>. While COBOL existed before the term Open Source was coined, it is now an open specification with a <a href="https://gnucobol.sourceforge.io/">GPL Compiler</a>. Everything has become open!</p>

<p>But...</p>

<p>There is still a stronghold of proprietary languages. <a href="https://uk.mathworks.com/products/matlab.html">MATLAB</a> is still heavily used by mathematicians and scientists. Languages like R are Open Source and making great in-roads. But MATLAB is popular.</p>

<p>The <a href="https://kx.com/">K Language</a> is widely used in financial firms.</p>

<p>Excel Macros, inexplicably, run the world.</p>

<p>Local scripting languages like AppleScript, and cloud languages like Google App Scripts are also closed - and can only run on proprietary software.</p>

<p>In the gaming world - while Lua and Godot and Blender are open - Unity is the big beast. And that's closed.</p>

<p>And, deep down in the silicon, the microcode running on your CPU, the blob in your Pi's multimedia decoder, and the baseband of your phone's modem are all locked down tighter than a whalebone corset.</p>

<p>But...</p>

<p>Even in those closed gardens, the weeds of Open Source take root. There are hundreds of Open Source libraries, plugins, and tutorials for all proprietary languages.  That doesn't get us to an open compiler or specification, but it brings the culture of Open Source with it.</p>

<p>So, has Open Source won? Mostly, I'd say. At the very least there are good Open Source alternatives to nearly every programming language and paradigm.</p>

<p>There are some niches - science, finance, gaming - which rely on proprietary languages. And Cloud services run custom software on their custom OS running on custom processors.  The new paradigm of LLM AI might eventually be fully open, but rely on processing power so out of reach of mortals that it might as well be closed.</p>

<p>But as more and more students become aware of Open Source, I hope that those last bastions of jealously guarded code will eventually fall.</p>

<p>Got any more examples of proprietary languages? <a href="https://mastodon.social/@Edent/110082980566230545">Join the discussion on Mastodon</a></p>

<div id="footnotes" role="doc-endnotes">
<hr>
<ol start="0">

<li id="fn:gordon">
<p>Flash may be dead but... <video muted="" autoplay="" loop=""><source src="https://shkspr.mobi/blog/wp-content/uploads/2023/03/gordons-alive.mp4" type="video/mp4" width="320">Clip from the movie "Flash Gordon". Brian Blessed's character says "Gordon's Alive!?"</video>&nbsp;<a href="https://shkspr.mobi/blog/2023/04/are-there-any-modern-closed-source-programming-languages/#fnref:gordon" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=45420&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/04/are-there-any-modern-closed-source-programming-languages/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[You can't screenshot or right click this image]]></title>
		<link>https://shkspr.mobi/blog/2022/12/you-cant-screenshot-or-right-click-this-image/</link>
					<comments>https://shkspr.mobi/blog/2022/12/you-cant-screenshot-or-right-click-this-image/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 10 Dec 2022 12:34:18 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[drm]]></category>
		<category><![CDATA[images]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=44057</guid>

					<description><![CDATA[People contact me with all sorts of weird opportunities. Some are fun. Some are not. I&#039;ve lost count of the number of NFT grifters who&#039;ve asked me to &#34;revolutionise&#34; the art space.  I&#039;m generally not a fan.  But I had one chat with someone who wanted to do something intriguing.  They were worried about people right-clicking or screenshotting their precious images and had a plan to stop that.  I…]]></description>
										<content:encoded><![CDATA[<p>People contact me with all sorts of <em>weird</em> opportunities. Some are fun. Some are not. I've lost count of the number of NFT grifters who've asked me to "revolutionise" the art space.  I'm generally not a fan.  But I had one chat with someone who wanted to do something intriguing.  They were worried about people right-clicking or screenshotting their precious images and had a plan to stop that.</p>

<p>I tried to explain to them that DRM <em>always</em> fails; you can't make data which can't be copied. I explained that artificial scarcity was harmful. They didn't care.</p>

<p>But, their proposed solution was intriguing. And, with their kind permission, I'm posting it here.  To be clear, I don't think this is <em>good</em> but I think it is vaguely interesting.</p>

<h2 id="interlacing"><a href="https://shkspr.mobi/blog/2022/12/you-cant-screenshot-or-right-click-this-image/#interlacing">Interlacing</a></h2>

<p>Back in the olden days, video was often <em>interlaced</em>. That is, the TV transmitter would broadcast the odd lines first, then the even lines. Your TV would draw the odd line then, in the millisecond before they faded away from the cathode-ray tube, draw the even lines.</p>

<p>Could we do the same with images?</p>

<p>Let's take this image of a Total Legitimate Bored Ape™
<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/11/ape.png" alt="A badly drawn cartoon of a monkey in a t-shirt." width="640" height="640" class="aligncenter size-full wp-image-44063"></p>

<p>With a scrap of Python, we can knock this into two interlaced images:</p>

<pre><code class="language-python">from PIL import Image
image = Image.open("ape.png")
pixels = image.load()
image.mode = "RGBA"

for y in range(image.height) :
   if (y%2 == 1) :
      for x in range(image.width) :
         pixels[x,y] = (0,0,0,0)

image.save("ape1.png")

for y in range(image.height) :
   if (y%2 == 0) :
      for x in range(image.width) :
         pixels[x,y] = (0,0,0,0)

image.save("ape2.png")
</code></pre>

<p>Which gives us:
<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/11/ape2.png" alt="Only the odd lines of the image are displayed." width="640" height="640" class="aligncenter size-full wp-image-44061" id="ape1"></p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/11/ape1.png" alt="Only the even lines of the image are displayed." width="640" height="640" class="aligncenter size-full wp-image-44062" id="ape2">

<p>Using JavaScript the first frame can be rendered onto a <code>&lt;canvas&gt;</code> element for a millisecond, wiped, and then the second displayed. Repeat.</p>

<p>Here's a demo of it in action - I kinda like the retro effect!</p>

<h3 id="%e2%9a%a0%ef%b8%8f-strobe-effect-warning"><a href="https://shkspr.mobi/blog/2022/12/you-cant-screenshot-or-right-click-this-image/#%e2%9a%a0%ef%b8%8f-strobe-effect-warning">⚠️ Strobe Effect Warning</a></h3>

<p><canvas id="cv" style="background-color: black;"></canvas></p>

<script>var image1  = document.getElementById("ape1"); var image2  = document.getElementById("ape2"); var canvas  = document.getElementById("cv"); canvas.width  = image1.width; canvas.height = image1.height;  var context = canvas.getContext("2d"); function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function drawInterlace() { frame = 0; while(true) { frame++; await sleep(1);  context.clearRect(0, 0, canvas.width, canvas.height); if (frame%2 == 0) {context.drawImage(image1, 0, 0); } else { context.drawImage(image2, 0, 0); } }  } drawInterlace();</script>

<p>If you right click on it, you'll only get half the image. If you take a screenshot, you'll only get half the image.  FOOLPROOF DRM!!!!!!</p>

<h2 id="expanding-the-complexity"><a href="https://shkspr.mobi/blog/2022/12/you-cant-screenshot-or-right-click-this-image/#expanding-the-complexity">Expanding the complexity</a></h2>

<p>Of course, you can <a href="https://stackoverflow.com/questions/20973528/canvas-disable-image-download">scramble an image before unscrambling it to the canvas</a> and all sort of other tricks.  You could have dozens of images rendering to the canvas in sequence.  You could draw individual pixels programmatically.  You could...</p>

<p>None of it matters of course. If you send pixels to a computer screen, they can be copied. It is possible to slow people down - but all it takes is for one person to work out how your scheme works and then it crumbles to dust.</p>

<p>As I said, I have a soft-spot for the shimmery effect. And I think it is kinda fun - in a code-golf way - to design obfuscation schemes.</p>

<p>But using anything like this for a form of digital restrictions management is daft.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=44057&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/12/you-cant-screenshot-or-right-click-this-image/feed/</wfw:commentRss>
			<slash:comments>14</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Book Review: The Programmer's Brain - What every programmer needs to know about cognition by Felienne Hermans ★★★★★]]></title>
		<link>https://shkspr.mobi/blog/2022/04/book-review-the-programmers-brain-what-every-programmer-needs-to-know-about-cognition-by-felienne-hermans/</link>
					<comments>https://shkspr.mobi/blog/2022/04/book-review-the-programmers-brain-what-every-programmer-needs-to-know-about-cognition-by-felienne-hermans/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 03 Apr 2022 11:34:05 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[Book Review]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[programming]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=42264</guid>

					<description><![CDATA[There are some books which make you feel smarter just by having them on your shelf. This is one of them! I would consider it essential for anyone working with code - whether a wide-eyed newbie or grizzled veteran.  How do human brains understand code? What neurological quirks do we all have? Which common mistakes can be easily avoided?  Only by understanding our puny hardware (&#34;Isn’t it a miracle …]]></description>
										<content:encoded><![CDATA[<p><img src="https://shkspr.mobi/blog/wp-content/uploads/2022/04/Hermans-HI.jpg" alt="Book cover for the Programmer's Brain." width="200" class="alignleft size-full wp-image-42289">There are some books which make you feel smarter just by having them on your shelf. This is one of them! I would consider it essential for anyone working with code - whether a wide-eyed newbie or grizzled veteran.</p>

<p>How do human brains understand code? What neurological quirks do we all have? Which common mistakes can be easily avoided?  Only by understanding our puny hardware ("Isn’t it a miracle that humans can do anything with no more than 1 byte of memory") can we understand how we should read, write, and <em>think</em> about code.</p>

<p>It is a relatively quick read - I burned through it on a 4 hour flight - but there are lots of practical exercises to do. And it is worth taking the time to think about <em>the process</em> of thinking about code.</p>

<p>Some of the insights are a little obvious - but it is worth spelling them out. For example:</p>

<blockquote><p>when debugging, many programmers prefer to make small changes to their code (tweaks) and run it again to see if the bug is fixed rather than spending the energy to create a good mental model of the problem</p></blockquote>

<p>It me! Why do I do that? Why do I think bashing around on a keyboard is more productive than taking 15 minutes to investigate my assumptions about what I'm trying to do - and what the previous programmer was trying to do?  The book has answers - and some practical guides for helping you correct your behaviour.</p>

<p>Even "simple" things like naming conventions get a thorough examination, with plenty of academic references:</p>

<blockquote><p>8.3.2 Snake case or camel case?
The results of Binkley’s study show that the use of camel case leads to higher accuracy among both programmers and non-programmers
8.4.1 Code with bad names has more bugs
Butler’s study found statistically significant associations between naming issues and code quality.</p></blockquote>

<p>There are lots of footnotes if you want to go wandering into the academic studies - and there are <em>many</em>.</p>

<p>The book - rightly - throws a bit of shade on those of us who have got into bad habits, but haven't reflected on whether they're actually working for us.</p>

<blockquote><p>11.2.5 Some thoughts on multitasking
The interesting thing is that people who multitask often <em>feel</em> very productive</p></blockquote>

<p>Ha! True. The book goes into the studies of why people feel the way they do - and whether the data bears out those feelings.</p>

<p>I urge you to read this book. If you are just getting started, it will help you develop good habits. If you're an occasional coder, it will keep you from straying. If you've been doing this for so long that your code is indecipherable to anyone else - it will give you a much-needed reality check.</p>

<p>The book is available <a href="https://www.manning.com/books/the-programmers-brain">DRM free from publisher</a> in PDF, ePub, and Mobi.  At about £25, it's probably worth charging it as a business expense.</p>

<p>Thanks to the publishers for the review copy.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=42264&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/04/book-review-the-programmers-brain-what-every-programmer-needs-to-know-about-cognition-by-felienne-hermans/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Smart Quotes and Coding Examples]]></title>
		<link>https://shkspr.mobi/blog/2021/11/smart-quotes-and-coding-examples/</link>
					<comments>https://shkspr.mobi/blog/2021/11/smart-quotes-and-coding-examples/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 13 Nov 2021 12:34:49 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[education]]></category>
		<category><![CDATA[NaBloPoMo]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=40956</guid>

					<description><![CDATA[Every so often, I copy and paste some code from a website and it utterly fails. This is probably a good reminder not to practise ✂️ &#38; 📋 development, but it is also a reminder that &#34;smart&#34; formatting often trips up new students.  Here are a few examples I&#039;ve seen recently - produced as a result of computers trying to be cleverer than humans, and humans not checking if computers are being stupid.  C…]]></description>
										<content:encoded><![CDATA[<p>Every so often, I copy and paste some code from a website and it utterly fails. This is probably a good reminder not to practise ✂️ &amp; 📋 development, but it is <em>also</em> a reminder that "smart" formatting often trips up new students.</p>

<p>Here are a few examples I've seen recently - produced as a result of computers trying to be cleverer than humans, and humans not checking if computers are being stupid.</p>

<h2 id="curly-quotes"><a href="https://shkspr.mobi/blog/2021/11/smart-quotes-and-coding-examples/#curly-quotes">Curly Quotes</a></h2>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2021/11/XSS.png" alt="Screenshot of an XSS attempt with smart quotes." width="867" height="53" class="aligncenter size-full wp-image-40960">

<p>Is this code valid?</p>

<p><code>&lt;a href=“example.html”&gt;read more&lt;/a&gt;</code></p>

<p>Yes! Let's read the <a href="https://html.spec.whatwg.org/multipage/syntax.html#attributes-2">HTML Specification on the syntax of attributes</a>:</p>

<blockquote><h2 id="unquoted-attribute-value-syntax"><a href="https://shkspr.mobi/blog/2021/11/smart-quotes-and-coding-examples/#unquoted-attribute-value-syntax">Unquoted attribute value syntax</a></h2>

<p>The attribute name, followed by zero or more ASCII whitespace, followed by a single U+003D EQUALS SIGN character, followed by zero or more ASCII whitespace, followed by the attribute value, which, in addition to the requirements given above for attribute values, must not contain any literal ASCII whitespace, any U+0022 QUOTATION MARK characters ("), U+0027 APOSTROPHE characters ('), U+003D EQUALS SIGN characters (=), U+003C LESS-THAN SIGN characters (&lt;), U+003E GREATER-THAN SIGN characters (&gt;), or U+0060 GRAVE ACCENT characters (`), and must not be the empty string.</p></blockquote>

<p>If you write <code>&lt;a href=example.html&gt;read more&lt;/a&gt;</code>, the HTML parser will take you to the page <code>example.html</code>.</p>

<p>But if you write <code>&lt;a href=“example.html”&gt;read more&lt;/a&gt;</code>, the parser will <em>literally</em> interpret the string and take you to the page <code>%E2%80%9Cexample.html%E2%80%9D</code></p>

<p>Smart quotes may work - but they may also behave unexpectedly.</p>

<p>The same problem occurs on the command line:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2021/11/Command-line.png" alt="Screenshot of a command line which has curly quotes." width="817" height="176" class="aligncenter size-full wp-image-40957">

<p>That's <code>sqlmap -u “URL” …</code> - some command line programs will ignore  the quotes - but most won't.</p>

<h2 id="a-grave-mistake"><a href="https://shkspr.mobi/blog/2021/11/smart-quotes-and-coding-examples/#a-grave-mistake">A grave mistake</a></h2>

<p>The same issue occurs with single quotes. Apostrophes become directional.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2021/11/SQL.png" alt="SQL statements where the quotes have become accent characters." width="698" height="183" class="aligncenter size-full wp-image-40959">

<p>Will this code work <code>SELECT * FROM whatever WHERE thing = `example´;</code> ? Nope!</p>

<p>We've known about these problems <a href="https://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html">for decades</a>.</p>

<h2 id="when-is-a-dash-not-a-dash"><a href="https://shkspr.mobi/blog/2021/11/smart-quotes-and-coding-examples/#when-is-a-dash-not-a-dash">When is a dash not a dash?</a></h2>

<p>On the command line, it's common to write <code>-h</code> or <code>--help</code>. That's either a single or double hyphen.</p>

<p>The problem is, there are <a href="https://www.compart.com/en/unicode/category/Pd"><em>lots</em> of typographical hyphens</a>. Some website publishing systems will "helpfully" convert a humble hyphen into an exotic "en-dash". While some word processors will take -- and transmogrify it into —.</p>

<p>This leads to code like <code>./foo --scan</code> becoming <code>./foo —scan</code></p>

<p>Consider this example:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2021/11/en-dash.png" alt="Screenshot of a command line invocation which has a mixture of dashes and en-dashes." width="1132" height="155" class="aligncenter size-full wp-image-40958">

<p>Can you tell which are en-dash and which are hyphens?</p>

<p>For a practical example, try running this code: <code>nmap –h</code> - how does it compare to <code>nmap -h</code> ?</p>

<h2 id="a-small-plea"><a href="https://shkspr.mobi/blog/2021/11/smart-quotes-and-coding-examples/#a-small-plea">A small plea</a></h2>

<p>Dear educators everywhere. Your students are going to copy and paste the code that you put in your examples. Before you publish that blog post, or send out that PowerPoint, please take five minutes to make sure the examples you include actually work properly.</p>

<p>And, to students, STOP COPYING AND PASTING CODE! You'll learn much more if you type things in yourself. By using tab-complete and judicious use of <code>--help</code>, you'll understand what the options are and what they do. That's much more important than mindless repetition.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=40956&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2021/11/smart-quotes-and-coding-examples/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[How not to do coding examples]]></title>
		<link>https://shkspr.mobi/blog/2021/06/how-not-to-do-coding-examples/</link>
					<comments>https://shkspr.mobi/blog/2021/06/how-not-to-do-coding-examples/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 03 Jun 2021 11:21:54 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[MSc]]></category>
		<category><![CDATA[r]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=39136</guid>

					<description><![CDATA[As part of my MSc, I&#039;m getting a few lessons in technologies I&#039;m not familiar with.  I&#039;ve found some of these lessons extremely confusing - even when I&#039;m proficient in the language.  Here&#039;s an example of a coding fragment from one of the tutorials in the R language.  Let me explain everything that I think is wrong with it.  barplot(H, names.arg =M, col=“blue” xlab =&#039;Country&#039;, ylab=&#34;Population&#34;) so…]]></description>
										<content:encoded><![CDATA[<p>As part of my MSc, I'm getting a few lessons in technologies I'm not familiar with.  I've found some of these lessons extremely confusing - even when I'm proficient in the language.</p>

<p>Here's an example of a coding fragment from one of the tutorials in the R language.  Let me explain everything that I think is wrong with it.</p>

<pre><code class="language-R">barplot(H, names.arg =M, col=“blue” xlab ='Country', ylab="Population")
something &lt;- lm( mydata$Col1~mydata$Col2)
</code></pre>

<p>What are <code>H</code> and <code>M</code>?  They are defined earlier in the document, but giving single character variable names  is disrespectful to readers of the code. We aren't in the mainframe era where we have limited memory and have to use single characters. We're not being charged per word here!</p>

<p>There is no justification for single character variables. Even in toy examples like the above. Take the time to respect your readers' limited time.</p>

<p>Next - what's with the inconsistent spacing on the <code>=</code> symbol?  Some languages are whitespace significant, others aren't. If you're a newbie to this language, do you automatically <em>know</em> whether R behaves weirdly if spaces aren't consistent?</p>

<p>Curly quotes! A sure sign that something has been written in a word processor which has "helpfully" turned a humble <code>"</code> into something more extravagant.  Again, if you're new to coding, will you instinctively understand why the copy-and-pasted example has failed?</p>

<p>Why do some strings use single quotes and some use double quotes? Can <code>"</code> be replaced by two apostrophes - <code>''</code>?</p>

<p><code>something</code> is not a suitable variable name for <em>anything</em>. The <code>lm()</code> function creates a linear model. If this is the student's first time using linear models, are they going to remember what <code>something</code> is when halfway through the exercise? For tutorial code, it almost always makes sense to give variable names a hint of what they contain. For example, <code>age_int</code> lets the reader know what the variable represents <em>and</em> what it contains.</p>

<p>Again <code>mydata</code> is pretty meaningless. Is this the data I created or the data I loaded from the CSV? Sure, <code>world_population_from_csv</code> is a bit wordy - but it is unambiguous in the context of a lesson.</p>

<p>And on and on it went.</p>

<p>As I happens, I was the only person in our tutorial with experience of R. Lest you think I'm exaggerating, I had to deal with all of the above when trying to help my fellow students understand what was going on.</p>

<p>If you understand a programming language, you are almost <em>guaranteed</em> to write an unsuitable tutorial the first time around. You first need to put the examples in front of people unfamiliar with your language, paradigm, or even the basics of programming. Let them explain to you what they find confusing so that you can write a better tutorial.</p>

<p>Yes, that's harder work for you. But your job is to make learning easier for others.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=39136&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2021/06/how-not-to-do-coding-examples/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Please Stop Inventing New Software Licences]]></title>
		<link>https://shkspr.mobi/blog/2020/09/please-stop-inventing-new-software-licences/</link>
					<comments>https://shkspr.mobi/blog/2020/09/please-stop-inventing-new-software-licences/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 09 Sep 2020 11:34:47 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[Open Source]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=36620</guid>

					<description><![CDATA[A few weeks ago, I received an unsolicited email inviting me to try out an exciting new &#34;quantum resistant&#34; cryptography app called Cyph. Because I hate myself, I signed up.  Of particular interest to me was the fact that the homepage loudly proclaimed that it was &#34;Open Source&#34; - and had a public GitHub repo.  That was handy, because there were some glaring usability flaws on the sign-up screen.…]]></description>
										<content:encoded><![CDATA[<p>A few weeks ago, I received an unsolicited email inviting me to try out an exciting new "<em>quantum resistant</em>" cryptography app called Cyph. Because I hate myself, I signed up.</p>

<p>Of particular interest to me was the fact that the homepage loudly proclaimed that it was "Open Source" - and had a public GitHub repo.  That was handy, because there were some glaring usability flaws on the sign-up screen. So, being a responsible nerd, I decided to send a pull request to fix them. Aren't I nice?!</p>

<p>The first thing I do before contributing is to check the software licence. I want to make sure the project I'm contributing to is <em>actually</em> open source.</p>

<p>This was not.</p>

<p>There are over <a href="https://opensource.org/licenses/alphabetical">a hundred different OSI Approved Licences</a>. For some reason, Cyph have invented their own proprietary licence!</p>

<p>This is a real barrier to getting a community to help with your project. People have to take time to read and understand an unfamiliar licence. If they have an employer, they may need to get their lawyers to approve any work done. A new licence is a hassle.</p>

<p>Cyph's licence was originally the <a href="https://referencesource.microsoft.com/license.html">Microsoft Reference Source Licence (MS-RSL)</a> - despite Cyph not being affiliated with Microsoft.  The licence says I am only allowed the source for reference use.  What does that mean?</p>

<blockquote><p>"Reference use" means use of the software within your company as a reference, in read only form, for the sole purposes of debugging your products, maintaining your products, or enhancing the interoperability of your products with the software, and specifically excludes the right to distribute the software outside of your company.</p></blockquote>

<p>This presents several problems.</p>

<ul>
<li>"Read Only Form" - if I make a fork on GitHub, or locally, my copy of the source will be writeable. Indeed, I will need to write to it in order to submit a PR.</li>
<li>Improving accessibility is not related to debugging, maintenance, or interoperability.</li>
<li>I do not have any products.</li>
<li>If I create a fork on GitHub, I will be distributing the code.</li>
<li>I am not a company, nor do I work for a company.</li>
</ul>

<p>Under the terms of their licence, I couldn't contribute back.  <a href="https://github.com/cyph/cyph/issues/64">I politely complained about this</a>. Cyph's response was to create their own licence! The brand-new <a href="https://github.com/cyph/cyph/commit/f2bb6a0b9c2a7e7eb6dce784a48423fb82fadd95">Cyph Reference Source Licence (CYPH-RSL)</a>.</p>

<p>The new licence is slightly better. It understands that I may be an individual rather than a company, and lets me take a copy of the code to test verifiable builds.  But the reference use becomes even more confusing.</p>

<blockquote><p>"Reference use" further includes use of the software within your organization, in writable form, for the sole purpose of developing and testing changes to the software that are intended to be transferred to the Licensor for consideration of inclusion within the software, and specifically excludes the right to distribute the software outside of your organization except as necessary for transference to the Licensor.</p></blockquote>

<p>What a tangled web!</p>

<p>What does "intended" mean here? Can I make a copy, make extensive changes, distribute my version of the code and <em>intend</em> to make it available - but never do so? How long a timeframe do I have? What if I work on something for years and <em>intend</em> to transfer it only when it is ready?</p>

<p>The wording still precludes me forking this repo on GitHub. That is not necessary for editing code and transference.</p>

<p>I cannot write a blog post about the changes I've made, because that will be distributing their software and isn't necessary for transference.</p>

<p>At which point, I decided to bow out of contributing. I was looking forward to improving its accessibility, and perhaps racking up some cheap <a href="https://hacktoberfest.digitalocean.com/">HacktoberFest points</a>.</p>

<p>It's great to challenge the status quo and invent new things. But when you do so, you risk causing confusion. And if you're trying to build a new community of contributors, that just adds friction.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=36620&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2020/09/please-stop-inventing-new-software-licences/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Do any Open Source Licences require source history?]]></title>
		<link>https://shkspr.mobi/blog/2020/08/do-any-open-source-licences-require-source-history/</link>
					<comments>https://shkspr.mobi/blog/2020/08/do-any-open-source-licences-require-source-history/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 01 Aug 2020 11:45:58 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[software]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=35960</guid>

					<description><![CDATA[A question to the void. Are you entitled to get the source history of open source projects?  Lots of Open Source licences give the consumer of software the right to a copy of the source code.  For example, GPLv3 says that distributors of software have to:  give anyone who possesses the object code ... a copy of the Corresponding Source  What is &#34;Corresponding Source&#34;?  The &#34;Corresponding Source&#34;…]]></description>
										<content:encoded><![CDATA[<p>A question to the void. Are you entitled to get the source <em>history</em> of open source projects?</p>

<p>Lots of Open Source licences give the consumer of software the right to a copy of the source code.</p>

<p>For example, <a href="https://www.gnu.org/licenses/gpl-3.0.txt">GPLv3</a> says that distributors of software have to:</p>

<blockquote><p>give anyone who possesses the object code ... a copy of the Corresponding Source</p></blockquote>

<p>What is "Corresponding Source"?</p>

<blockquote><p>The "Corresponding Source" means all the source code needed to generate, install, and run the object code</p></blockquote>

<p>That, to me, reads like a user is only entitled to a code snap-shot. Not the full history of code development, but just the version of the source used to compile the distributed binaries.</p>

<p>My question to you, gentle reader - are there any licences which compel the distribution or publication of the development history?</p>

<h2 id="is-this-a-good-idea"><a href="https://shkspr.mobi/blog/2020/08/do-any-open-source-licences-require-source-history/#is-this-a-good-idea">Is this a good idea?</a></h2>

<p>Services like GitHub and GitLab now dominate the open source conversation. But nothing about FOSS says that you <em>must</em> develop in public. It just seems to be the social norm now.</p>

<p>But I don't see any reason to make it a compulsory part of a licence.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=35960&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2020/08/do-any-open-source-licences-require-source-history/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Relaunching @edent_solar. Part 3 - API & Code]]></title>
		<link>https://shkspr.mobi/blog/2020/03/relaunching-edent_solar-part-3-api-code/</link>
					<comments>https://shkspr.mobi/blog/2020/03/relaunching-edent_solar-part-3-api-code/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 28 Mar 2020 12:57:55 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[fronius]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[solar]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=34491</guid>

					<description><![CDATA[I&#039;m hooking my solar panels up to Twitter!   Installation Inverter   Fronius provide a comprehensive API guide - I wish more companies did this. There are lots of unofficial libraries in a variety of different languages. I&#039;ve written this code in Python3.  This is a general tidy-up of the code I wrote several years ago.  Here&#039;s how it works....   Every minute, the script runs from crontab. If it…]]></description>
										<content:encoded><![CDATA[<p>I'm hooking my solar panels up to Twitter!</p>

<ol>
<li><a href="https://shkspr.mobi/blog/2020/03/relaunching-edent_solar-part-1-installation/">Installation</a></li>
<li><a href="https://shkspr.mobi/blog/2020/03/relaunching-edent_solar-part-2-inverter/">Inverter</a></li>
</ol>

<p>Fronius provide <a href="https://www.fronius.com/~/downloads/Solar%20Energy/Operating%20Instructions/42%2C0410%2C2012.pdf">a comprehensive API guide</a> - I wish more companies did this. There are lots of unofficial libraries in a variety of different languages. I've written this code in Python3.</p>

<p>This is a general tidy-up of the code I wrote several years ago.</p>

<p>Here's how it works....</p>

<ul>
<li>Every minute, the script runs from <code>crontab</code>.</li>
<li>If it is after sunrise and before sunset, it takes a reading and writes it to today's CSV file.</li>
<li>If it is after sunset, draw a graph, save it, Tweet it.

<ul>
<li>Only do this once.</li>
</ul></li>
</ul>

<p>This is in Python3 so should run on a Raspberry Pi - or any other computer.</p>

<p>If you are running this code on the same network as your Fronius, there is no security. The API lets you read data without a password.</p>

<p>You can <a href="https://github.com/edent/Fronius-DataManager-Solar-Logger/blob/master/fronius.py">get the latest code on GitHub</a>.</p>

<h2 id="output"><a href="https://shkspr.mobi/blog/2020/03/relaunching-edent_solar-part-3-api-code/#output">Output</a></h2>

<blockquote class="social-embed" id="social-embed-1243242375172034563" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"><header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><a href="https://twitter.com/Edent_Solar" class="social-embed-user" itemprop="url"><img class="social-embed-avatar social-embed-avatar-square" src="data:image/webp;base64,UklGRqIBAABXRUJQVlA4IJYBAAAQCwCdASowADAAPpk+lkmlo6IhPHn4ALATCWwAqSb2fKyIqg/VfwXvoZTeujRmaZz6AAQqKO30BbgOJGUvMk1saJobx5uEgICp7ECZe2hnMnGIEtdZ1IpWDe69a2H1qfOgAP79LwB5Oz3xDljcr2v6LuhchyOjYT2dLq9Fp2efm/jgtH4ejNYBrnBqmHREwRg+HtdB/egiabaMf7s0gyPxrKDO9vD/6+LdU39pQgXiwpu/LVAjihuJ4gpq98Gdnn2pmyHjmfljnEcdBTY1FJ6Kc41+qjuplI2wOjZs7uPzUOBsZ17JEKid9H/24DLriHA1/yF9UrE8W1TFS/7qf/zN3TfFR6/9fEZ3BNaFz3zUbc0uv9qsPWkTrRpwkAV4xS23X6PmDD3d87QYdoeuEZw10Tk6yvv7H3xqtevEG/XYu42BFkyWslT2hd3NDZXKhdUP7HqdiFENKuCe8a47ssoTP1fgvX/oVjnf9AnlsjZIw+FtuMaaXyEip9us2NbB//qP7Ia6+M5rcWnPv/rbeZONTQ9fj6AA" alt="" itemprop="image"><div class="social-embed-user-names"><p class="social-embed-user-names-name" itemprop="name">Edent's Solar Panels</p>@Edent_Solar<br><img src="data:image/webp;base64,UklGRuICAABXRUJQVlA4WAoAAAAQAAAAWQAAWQAAQUxQSOEBAAABkFXbbt1agmAIhmAIYtAyiBnEDBIGDYOEQQ+CawiHQcWgZlB9NPVD+76/ImIC6P9mf43z+h45/Ex4y0Xr88Y/Bd6KdpU9oHHWgXlC4qyDs0dxNzV4cxBB1KR4gKmo0ZLMLWp4Mbao6cXUosYXQ7Oan8z4Yq94K6KA4mwsCnkz4RU0WLijZAOssDwu4+RhXoHDqANpHSVIZVBQaB6TsNKYjPU1pmCVIU7B3QhGCyOuaJcevO3TW0SLROTmffMVk6qquB5ln+Oa++Q1zof0cKKqmj44edO1w+ro3UtbZjpdOyR9L+6M9fSrjenz0XLQ52tbPtHQkJsSVTqpE6pNvXiMUDXXTVUkCKmOSo1Q/YrADfea3HDFO2r2Bka4NNxrvvDWBqkpDQnhu461musEQVOV1H27mlkhSqjYtXGvCIqhupz5rM3Zn7hFYVT2Oc5Zu+Y1xq0okPn/4MJP5d5AcjL9HOLJgz4GUdWDfg50e71ez/CJ/DUy/SyIY3TUEe7y1jug8QiH5kaQYAkN3bD2MYx1GUMFSWjwipRGuYLz8KMo4Uw0PqP8IINeMB7eAoWC8AxkMxR7z0BWg1h7BLLrs6nXD0+mo9h5JjIfxcTruTpC5OO73+v0cWPCdcyx+zU4+ucNAFZQOCDaAAAAEAkAnQEqWgBaAD6xQJZJpyOhoTHfKgDgFglnAMsJn9f5RA9neHFaHPCHQSI1eR9V12wtRy3QA5cP9kU/SRnvQvGNNjncx9YB6Kuo7e/ddDIQAP71a7X9lf//7inBZ9+1bUYtAD/+4H/+ze//7twx0VpJlQAP9a4v5WQ+ueBp9Y03AfiquNTvm13Evju2XcfY+o3HMiK3+95AniJwk3Adjj40hAQ6EHIBHw2AMqn3esgxQUf+wJVshLIRjKbpOQ+On/6AC4/u/dSILuynNzErp6Eq/7wN58bpgAA=" alt="" class="social-embed-badge"> Automated</div></a><img class="social-embed-logo" alt="Twitter" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0Aaria-label%3D%22Twitter%22%20role%3D%22img%22%0AviewBox%3D%220%200%20512%20512%22%3E%3Cpath%0Ad%3D%22m0%200H512V512H0%22%0Afill%3D%22%23fff%22%2F%3E%3Cpath%20fill%3D%22%231d9bf0%22%20d%3D%22m458%20140q-23%2010-45%2012%2025-15%2034-43-24%2014-50%2019a79%2079%200%2000-135%2072q-101-7-163-83a80%2080%200%200024%20106q-17%200-36-10s-3%2062%2064%2079q-19%205-36%201s15%2053%2074%2055q-50%2040-117%2033a224%20224%200%2000346-200q23-16%2040-41%22%2F%3E%3C%2Fsvg%3E"></header><section class="social-embed-text" itemprop="articleBody">Today I generated 16.19kWh of solar electricity. <a href="https://x.com/Edent_Solar/status/1243242375172034563/photo/1">pic.x.com/puH8Pwfbev</a><div class="social-embed-media-grid"><a href="https://pbs.twimg.com/media/EUDiWYZXYAAtpHQ.png" class="social-embed-media-link"><img class="social-embed-media" alt="" src="data:image/webp;base64,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"></a></div></section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/Edent_Solar/status/1243242375172034563"><span aria-label="0 likes" class="social-embed-meta">❤️ 0</span><span aria-label="1 replies" class="social-embed-meta">💬 1</span><span aria-label="0 reposts" class="social-embed-meta">🔁 0</span><time datetime="2020-03-26T18:24:15.000Z" itemprop="datePublished">18:24 - Thu 26 March 2020</time></a></footer></blockquote>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=34491&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2020/03/relaunching-edent_solar-part-3-api-code/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Add review to Goodreads from Schema markup]]></title>
		<link>https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/</link>
					<comments>https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#respond</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 10 Dec 2019 19:40:23 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[goodreads]]></category>
		<category><![CDATA[hack]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[schema.org]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=33209</guid>

					<description><![CDATA[I write book reviews on my blog. I also want to syndicate them to Goodreads.  Sadly, Goodreads doesn&#039;t natively read the Schema.org markup I so carefully craft.  So here&#039;s the scrap of code I use to syndicate my reviews.  Goodreads API Keys  Get your Keys from https://www.goodreads.com/api/keys  You will also need to get OAuth tokens  For this documentation, I&#039;ll use the example keys - please…]]></description>
										<content:encoded><![CDATA[<p>I write book reviews on my blog. I also want to syndicate them to Goodreads.</p>

<p>Sadly, Goodreads doesn't natively read the Schema.org markup I so carefully craft.  So here's the scrap of code I use to syndicate my reviews.</p>

<h2 id="goodreads-api-keys"><a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#goodreads-api-keys">Goodreads API Keys</a></h2>

<p>Get your Keys from <a href="https://www.goodreads.com/api/keys"></a><a href="https://www.goodreads.com/api/keys">https://www.goodreads.com/api/keys</a></p>

<p>You will also need to <a href="https://www.goodreads.com/api/oauth_example#python">get OAuth tokens</a></p>

<p>For this documentation, I'll use the example keys - please substitute them with your own keys.</p>

<pre><code class="language-python">from rauth.service import OAuth1Service, OAuth1Session

# Get a real consumer key &amp; secret from: https://www.goodreads.com/api/keys
API_KEY    = 'ABC123'
API_SECRET = 'XYZ789'

goodreads = OAuth1Service(
    consumer_key    = API_KEY,
    consumer_secret = API_SECRET,
    name='goodreads',
    request_token_url = 'https://www.goodreads.com/oauth/request_token',
    authorize_url     = 'https://www.goodreads.com/oauth/authorize',
    access_token_url  = 'https://www.goodreads.com/oauth/access_token',
    base_url          = 'https://www.goodreads.com/'
)

OAUTH_TOKEN, OAUTH_SECRET = goodreads.get_request_token(header_auth=True)
authorize_url = goodreads.get_authorize_url(request_token)
print(authorize_url)
</code></pre>

<p>Visit the URL printed out, and authorise the app.  Then run this to get the access tokens you need:</p>

<pre><code class="language-python">session = goodreads.get_auth_session(OAUTH_TOKEN, OAUTH_SECRET)
ACCESS_TOKEN = session.access_token
ACCESS_TOKEN_SECRET = session.access_token_secret
</code></pre>

<p>Save those tokens, we'll need them later!</p>

<h2 id="get-json-ld-from-html-page"><a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#get-json-ld-from-html-page">Get JSON-LD from HTML page</a></h2>

<p>This uses <a href="https://docs.python-requests.org/en/latest/index.html">Requests</a> and <a href="https://pypi.org/project/beautifulsoup4/">BeautifulSoup</a>.</p>

<pre><code class="language-python">import requests
from bs4 import BeautifulSoup
import json
import re

review_url = 'https://shkspr.mobi/blog/2019/11/review-because-internet-by-gretchen-mcculloch/'
r = requests.get(review_url)
soup = BeautifulSoup(r.text)
pattern = re.compile(r"\"@type\":\"Review\"")
script = soup.find("script", text=pattern)
review = script.text
review_json = json.loads(review)

isbn        = review_json["itemReviewed"]["isbn"]
rating      = review_json["reviewRating"]["ratingValue"]
date        = review_json["datePublished"]:date[0:10]
description = review_json["description"] + " " + review_url

</code></pre>

<h2 id="isbn-to-goodreads-id"><a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#isbn-to-goodreads-id">ISBN to Goodreads ID</a></h2>

<pre><code class="language-python">goodreads_id = requests.get("https://www.goodreads.com/book/isbn_to_id/" + isbn + "?key=" + API_KEY).text
</code></pre>

<p>This will return a number - the Goodreads ID.</p>

<h2 id="post-a-review"><a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#post-a-review">Post a review</a></h2>

<p>The <a href="https://www.goodreads.com/api/index#review.create">documentation for posting Goodreads reviews</a> is a bit sparse.</p>

<pre><code class="language-python">goodreads_api = OAuth1Session(
    consumer_key        = API_KEY,
    consumer_secret     = API_SECRET,
    access_token        = ACCESS_TOKEN,
    access_token_secret = ACCESS_TOKEN_SECRET,
)

#   Post the review to the API
#   https://www.goodreads.com/api/index#review.create

review_data = {"book_id": goodreads_id, "review[review]":description, "review[rating]":rating, "review[read_at]":date}

response = goodreads_api.post('https://www.goodreads.com/review.xml', review_data)

print(response.text)
</code></pre>

<h2 id="setting-the-date"><a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#setting-the-date">Setting the date</a></h2>

<p>The Goodreads API is... crap. <a href="https://www.goodreads.com/topic/show/20997772-review-read-at-doesn-t-work">Posting the review doesn't actually add a date to the review</a>. So you need to edit the review to post it <em>again</em>.  To start with, we need to get the review's ID:</p>

<pre><code class="language-python">import xml.etree.ElementTree as ET
tree = ET.fromstring(response.text)
review_id = tree.find("id").text
review_data = {"review[read_at]":date[0:10]}
response = goodreads_api.post('https://www.goodreads.com/review/'+review_id+'.xml', review_data)
print(response.text)
</code></pre>

<h2 id="get-the-code"><a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#get-the-code">Get the code</a></h2>

<p><a href="https://gitlab.com/edent/schema-to-goodreads/tree/master">The Python code is available on my GitLab</a>.</p>

<p>But... The Goodreads API is a bit picky. ISBN lookup doesn't work very well, and bits of the API are flaky. So while this code <em>mostly</em> works, I don't run it that often.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=33209&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Redirect GitHub ID to Username]]></title>
		<link>https://shkspr.mobi/blog/2018/11/redirect-github-id-to-username/</link>
					<comments>https://shkspr.mobi/blog/2018/11/redirect-github-id-to-username/#respond</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 22 Nov 2018 12:01:00 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[hack]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[NaBloPoMo]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=30651</guid>

					<description><![CDATA[Scratching my own itch here...  GitHub users have a username (mine is @edent) and have a user ID number (mine is #837136).  If you want to redirect a user ID to a username, you can use the little service I&#039;ve cobbled together:  https://edent.github.io/github_id/#837136  That will take your browser to my GitHub page, using nothing but my ID.  Why?   Some login services only give you the GitHub…]]></description>
										<content:encoded><![CDATA[<p>Scratching my own itch here...</p>

<p>GitHub users have a username (mine is <code>@edent</code>) <em>and</em> have a user ID number (mine is <code>#837136</code>).</p>

<p>If you want to redirect a user ID to a username, you can use the little service I've cobbled together:</p>

<p><a href="https://edent.github.io/github_id/#837136">https://edent.github.io/github_id/#837136</a></p>

<p>That will take your browser to my GitHub page, using nothing but my ID.</p>

<h2 id="why"><a href="https://shkspr.mobi/blog/2018/11/redirect-github-id-to-username/#why">Why?</a></h2>

<ul>
<li>Some login services only give you the GitHub user's ID.</li>
<li>GitHub users can change their username - but their ID stays the same.</li>
</ul>

<h2 id="how"><a href="https://shkspr.mobi/blog/2018/11/redirect-github-id-to-username/#how">How?</a></h2>

<p>Inspired by <a href="http://caius.name/">Caius Durling</a>'s useful <a href="http://caius.github.io/github_id/">GitHub username to ID</a> service.</p>

<p>Mine is a scrap of JavaScript which uses <a href="https://api.github.com/user/837136">this undocumented endpoint</a> to get user info.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2018/10/GitHub-API-fs8.png" alt="A screen of JSON code showing my details." width="596" height="354" class="aligncenter size-full wp-image-30652">

<pre><code class="language-javascript">&lt;script type="text/javascript" charset="utf-8"&gt;
function get_github_name_for(id) {
    $.getJSON('https://api.github.com/user/' + id + "?callback=?", 
    function(json){
        var github_user_url = json["data"]["html_url"]
        window.location.replace(github_user_url)
        return false
    });
}
$(document).ready(function() {
    if (document.location.hash) {
        var id = document.location.hash.match(/^#(.*?)$/)[1]
        get_github_name_for(id)
    }
});
&lt;/script&gt;
</code></pre>

<p>Feel free to copy it, or use <a href="https://edent.github.io/github_id/#837136">https://edent.github.io/github_id/#837136</a> as a handy little webservice.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=30651&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2018/11/redirect-github-id-to-username/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Previewing Circular Avatars]]></title>
		<link>https://shkspr.mobi/blog/2017/07/previewing-circular-avatars/</link>
					<comments>https://shkspr.mobi/blog/2017/07/previewing-circular-avatars/#respond</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 02 Jul 2017 11:04:05 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[avatar]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[HowTo]]></category>
		<category><![CDATA[mvp]]></category>
		<category><![CDATA[Social Networks]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=25441</guid>

					<description><![CDATA[Play with the demo  I was lucky enough to go to Number 10 Downing Street for a meeting a few weeks ago.  A perfect opportunity for a photo of me by that famous door.    Being the vain man that I am, I wanted to set it as my avatar photo.  But Twitter and other social networks now use circular avatars.    The &#34;10&#34; has been obliterated and part of my head is missing.  So I&#039;ve created a quick way to …]]></description>
										<content:encoded><![CDATA[<p><a href="https://edent.github.io/AvatarPreview/">Play with the demo</a></p>

<p>I was lucky enough to go to Number 10 Downing Street for a meeting a few weeks ago.  A perfect opportunity for a photo of me by that famous door.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2017/07/Terence-at-Number-10-Square.jpg" alt="Terence at Number 10 Downing Street" width="256" height="256" class="aligncenter size-full wp-image-25443" style="border-radius:0%">

<p>Being the vain man that I am, I wanted to set it as my avatar photo.  But Twitter and other social networks now use circular avatars.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2017/07/Terence-at-Number-10-Circle.jpg" alt="Terence at Number 10 - the door number is obscured as is part of his face because the photo has been cropped in a circle" width="256" height="256" class="aligncenter size-full wp-image-25442" style="border-radius:0%">

<p>The "10" has been obliterated and part of my head is missing.</p>

<p>So I've created a quick way to preview avatar images to see how they'll look on both squares and circles.  It places a simple overlay on top of your image - like so:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2017/07/Guidelines.jpg" alt="The same photo - but with a red circular guideline superimposed" width="262" height="262" class="aligncenter size-full wp-image-25444" style="border-radius:0%">

<p>Images can be panned around and zoomed using the mouse.</p>

<p>You can <a href="https://edent.github.io/AvatarPreview/">play with the demo</a></p>

<iframe src="https://edent.github.io/AvatarPreview/" width="100%" height="400px"></iframe>

<p>This is only a Minimum Viable Prototype - pull requests welcome!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=25441&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2017/07/previewing-circular-avatars/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Get your Google Location History the hard way… Again!]]></title>
		<link>https://shkspr.mobi/blog/2015/09/get-your-google-location-history-the-hard-way-again/</link>
					<comments>https://shkspr.mobi/blog/2015/09/get-your-google-location-history-the-hard-way-again/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 02 Sep 2015 12:17:03 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[location]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=21334</guid>

					<description><![CDATA[Last year, I wrote about how to extract Location History from Google.  Once again, Google have changed their URLs to make it even harder to get one&#039;s current location out of their data-greedy hands.  It used to be the case that Latitude gave that information - but they killed it.  Then they promised it in Google+ - but never delivered.  Now they offer you a data-dump which they will email to you. …]]></description>
										<content:encoded><![CDATA[<p>Last year, I wrote about <a href="https://shkspr.mobi/blog/2014/04/extracting-your-own-location-information-from-google-the-hard-way/">how to extract Location History from Google</a>.  Once again, Google have changed their URLs to make it even harder to get one's current location out of their data-greedy hands.</p>

<p>It used to be the case that Latitude gave that information - but they killed it.  Then they promised it in Google+ - but never delivered.  <em>Now</em> they offer you a data-dump which they will email to you.  Hardly convenient if you want a single day!</p>

<p><em>sigh</em></p>

<p>Recently <a href="https://www.google.com/maps/timeline">Google released Timeline</a> - an interactive way to view where you've been.  It also offers the ability to download a single day's worth of location tracks as KML.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2015/09/Google-Timeline-KML-fs8.png" alt="Google Timeline KML-fs8" width="589" height="465" class="aligncenter size-full wp-image-21335">

<p>With a little bit of fiddling, we can get the direct URL. Strap in, it's going to get rough!</p>

<p>In order to get today's date (2015-09-02) the URL needs to be:</p>

<p><code>https://www.google.com/maps/timeline/kml?authuser=0&amp;pb=!1m8!1m3!1i2015!2i8!3i2!2m3!1i2015!2i8!3i2</code></p>

<p>That URL format is… unique…</p>

<p>Here's how it breaks down:</p>

<p><code>pb=!1m8!1m3!1i<strong>YYYY</strong>!2i<strong>MM</strong>!3i<strong>DD</strong>!2m3!1i<strong>YYYY</strong>!2i<strong>MM</strong>!3i<strong>DD</strong></code></p>

<p>Where <strong>MM</strong> is a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date" rel="nofollow">Javascript style zero-based month</a>.  (Why is the first month "0" but the first day "1"? <a href="https://twitter.com/0xabad1dea/status/605553613558173696">Because</a> <a href="http://stackoverflow.com/questions/2552483/why-does-the-month-argument-range-from-0-to-11-in-javascripts-date-constructor">fuck</a> <a href="https://web.archive.org/web/20200924204658/https://twitter.com/benjaminpearson/status/7348991893">you</a>, <a href="http://programmers.stackexchange.com/questions/179285/why-does-javascript-treat-days-and-months-differently">that's</a> <a href="https://web.archive.org/web/20200924204704/https://twitter.com/Latish/status/634795697460514816">why</a>!)</p>

<p>One thing to note, when you get the KML file, the timestamps are in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601 format</a> (Yay!) but are set to West Coast USA time (Boo!)
<code>2015-09-02T12:15:37.836-07:00</code></p>

<p>Annoying, but not the end of the world.  How do you get the data into your app?</p>

<p>You <strong>can't</strong>.</p>

<p>There's no authorised API access for this.  You can't do the OAuth dance.  The only way is via cookie-jacking.</p>

<p>Sign in to Google via your web browser.  Open up your web inspector in Chrome or Firefox.  Load the KML URL and choose "<a href="http://www.lornajane.net/posts/2013/chrome-feature-copy-as-curl">Copy as cURL</a>"
<img src="https://shkspr.mobi/blog/wp-content/uploads/2015/09/Cookie-Jacking-fs8.png" alt="Cookie Jacking-fs8" width="1024" height="299" class="aligncenter size-full wp-image-21338"></p>

<p>You'll end up with a command like:</p>

<pre>curl 'https://www.google.com/maps/timeline/kml?authuser=0&amp;pb=!1m8!1m3!1i2015!2i8!3i2!2m3!1i2015!2i8!3i2'
 -H 'Host: www.google.com'
 -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0'
 -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
 -H 'Accept-Language: en-GB,en;q=0.5'
 --compressed
 -H 'DNT: 1'
 -H 'Cookie: --------------'
 -H 'Connection: keep-alive'</pre>

<p>You can use that command - or steal your own cookie - for use in your app.  Your cookie will likely expire after a set time - whereupon you'll have to reauthenticate.</p>

<p>What a faff!  Come on, Google. Rather than pissing about with your logo, why not build something that works properly? I dare ya!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=21334&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2015/09/get-your-google-location-history-the-hard-way-again/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Why I vertically align my code (and you should too!)]]></title>
		<link>https://shkspr.mobi/blog/2014/11/why-i-vertically-align-my-code-and-you-should-too/</link>
					<comments>https://shkspr.mobi/blog/2014/11/why-i-vertically-align-my-code-and-you-should-too/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 29 Nov 2014 11:23:56 +0000</pubDate>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[NaBloPoMo]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[style]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=20087</guid>

					<description><![CDATA[There was an interesting discussion on HackerNews last week about the Linux Kernel coding style.  During the discussion, I kicked off a minor holy war about whether one should vertically align code.  I&#039;m all for it!  Let me explain why.  What Is Vertical Alignment?  Take this trivial example:  int robert_age = 32; int annalouise_age = 25; int bob_age = 250; int dorothy_age = 56;  I find easier to …]]></description>
										<content:encoded><![CDATA[<p>There was an <a href="https://news.ycombinator.com/item?id=8661740">interesting discussion on HackerNews</a> last week about the <a href="https://www.kernel.org/doc/Documentation/CodingStyle">Linux Kernel coding style</a>.</p>

<p>During the discussion, I kicked off a minor holy war about <a href="https://news.ycombinator.com/item?id=8662276">whether one should vertically align code</a>.  I'm all for it!  Let me explain why.</p>

<h2 id="what-is-vertical-alignment"><a href="https://shkspr.mobi/blog/2014/11/why-i-vertically-align-my-code-and-you-should-too/#what-is-vertical-alignment">What Is Vertical Alignment?</a></h2>

<p>Take this trivial example:</p>

<pre>int robert_age = 32;
int annalouise_age = 25;
int bob_age = 250;
int dorothy_age = 56;</pre>

<p>I find easier to read as:</p>

<pre>int robert_age     = 32;
int annalouise_age = 25;
int bob_age        = 250;
int dorothy_age    = 56;</pre>

<p>At a glance I can see that "bob_age" is unusual.  I can easily see that everything there is an integer without having to slide my eyes around.</p>

<p>This view is not <a href="http://programmers.stackexchange.com/questions/30029/vertical-alignment-yea-or-nay">universally</a> <a href="http://www.zeyalabs.ch/posts/2013/vertical-alignment-sucks/">shared</a> - so I shall attempt to explain why <a href="https://web.archive.org/web/20150601011243/http://www.andrewewhite.net/wordpress/2010/09/24/writing-beautiful-code-vertical-alignment-coding-style/">many</a> <a href="https://web.archive.org/web/20150124055741/http://francoishill.fr/aligning-patterns-in-code/">people</a> think it is a useful style guide.</p>

<h2 id="comprehension"><a href="https://shkspr.mobi/blog/2014/11/why-i-vertically-align-my-code-and-you-should-too/#comprehension">Comprehension</a></h2>

<p>90% of programming is problem solving.  <a href="http://en.wikipedia.org/wiki/Ninety-ninety_rule">The other 90%</a> is understanding how the problem was solved.</p>

<p>Reading code isn't much different from reading prose.  We expect authors to explain their arguments clearly, not be overly verbose in their extraneous use of their chosen language, and 2 respect da common grmmatcal style.</p>

<p>Indeed, the Kernel coding style emphasises this heavily.  How you choose a variable name is as important as what the code does.</p>

<p>Consider the following line of code:</p>

<pre>var thinG=doIt(thestuff,MORE_sTuff); /* LOL! */</pre>

<p>Even if you deeply understood the codebase, that's not a particularly readable line of code.</p>

<pre>var totalBill = apply_tax(initialBill, taxRate);</pre>

<p>With a sensible application of naming conventions, spacing, and capitalisation, we've made the code much easier to read.  That means the poor sap who inherits our code will spend less time <em>deciphering</em> it and more time <em>understanding</em> it.</p>

<h2 id="why-use-monospace-fonts"><a href="https://shkspr.mobi/blog/2014/11/why-i-vertically-align-my-code-and-you-should-too/#why-use-monospace-fonts">Why Use Monospace Fonts?</a></h2>

<p>As with all good old-fashioned flamewars, there are two equally passionate sides on the monospace vs proportional font debate.</p>

<p>Some heretical scum will tell you that <a href="https://web.archive.org/web/20150124145301/https://code.google.com/p/i3project/wiki/Fonts">proportional</a> <a href="https://web.archive.org/web/20140913135726/http://nickgravgaard.com/elastictabstops/news/programming-fonts/">fonts</a> are the <a href="http://www.slant.co/topics/67/~what-are-the-best-programming-fonts">best</a> - ignore these heathens.
Others will sow your mind with discord as they argue for the eugenic <a href="https://news.ycombinator.com/item?id=4623781">purity</a> of <a href="http://blog.codinghorror.com/revisiting-programming-fonts/">proportional</a> <a href="http://programmers.stackexchange.com/questions/5473/does-anyone-prefer-proportional-fonts">fonts</a> - those poor condemned souls.</p>

<p>Ultimately, it comes down to readability.  What do you find easiest to aid your comprehension of the code?  It's why IDEs have colour schemes - so you can tell at a glance whether "foo" is a function, a constant, a variable, or a comment.  Anything which makes it quicker for you to comprehend what a block of code does is <em>a good thing!</em></p>

<p>This is one of the reasons spreadsheets are so popular.  Columns aid readability.  You can quickly scan your eyes down a column and notice if a row is significantly different to its siblings.</p>

<h2 id="we-dont-have-the-tools"><a href="https://shkspr.mobi/blog/2014/11/why-i-vertically-align-my-code-and-you-should-too/#we-dont-have-the-tools">We Don't Have The Tools</a></h2>

<p>Interestingly, the big criticism I faced in the HN discussion was not about whether vertical alignment is useful - but around how poor our tools are.</p>

<blockquote><p>This ruins the readability and usability of your diffs. Say you need to quickly track down a major bug due to a change in a single constant. With horizontal alignment, the diff might contain any number of changed lines, obscuring the crucial change. There are workarounds that ignore whitespace and word-based diffs, but it's just not worth the trouble IMHO.
</p><p><cite><a href="https://news.ycombinator.com/item?id=8662938">Andreas van Cranenburgh</a></cite>
</p></blockquote>

<p>...And...</p>

<blockquote><p>If you have, say, 50 lines of assignment and you align all the values to the largest one, adding one forces you to update 50 lines. I've been faced with those very situations and that is when I understood how important it is not to align values like that.
</p><p><cite><a href="https://news.ycombinator.com/item?id=8665741">scrollaway</a></cite>
</p></blockquote>

<p>Those arguments are valid - in certain contexts - but point to the need for better tooling.</p>

<p>I'm reminded of the idea of <a href="http://nickgravgaard.com/elastictabstops/">Elastic Tabstops</a> - a method to auto align code blocks:
</p><div id="attachment_20089" style="width: 403px" class="wp-caption aligncenter"><a href="http://nickgravgaard.com/elastictabstops/"><img aria-describedby="caption-attachment-20089" src="https://shkspr.mobi/blog/wp-content/uploads/2014/11/columnblocks_coloured.gif" alt="By Nick Gravgaard" width="393" height="255" class="size-full wp-image-20089"></a><p id="caption-attachment-20089" class="wp-caption-text">By Nick Gravgaard</p></div><p></p>

<p>Our tools can easily accommodate this way of working.  Computers are there to do boring and repetitive jobs for us - and CPU cycles are cheap enough to "waste" on making our code more readable.</p>

<p>There are <a href="https://github.com/torvalds/linux/blob/9a3c4145af32125c5ee39c0272662b47307a8323/net/wireless/wext-proc.c#L135">plenty</a> of <a href="https://github.com/torvalds/linux/blob/cba3b00deab5a8564d61ec18e61ba6ba82203299/include/uapi/sound/asound.h">examples</a> in the <a href="https://github.com/torvalds/linux/blob/9a3c4145af32125c5ee39c0272662b47307a8323/arch/mips/include/asm/octeon/cvmx-bootmem.h">Linux</a> <a href="https://github.com/torvalds/linux/blob/c6c9161d064d30e78904f3affe5184487493e0fc/arch/x86/kernel/cpu/common.c#L103">Kernel</a> where vertical alignment is used to make code easier for humans to parse.</p>

<p>Vertical alignment doesn't work in every context - but for quickly assessing data, its readability is unparalleled.</p>

<p>Coding is a creative medium through which we express our ideas.  If our tools make understanding those ideas more difficult, it's the tools which need to change - not us.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=20087&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2014/11/why-i-vertically-align-my-code-and-you-should-too/feed/</wfw:commentRss>
			<slash:comments>34</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Extracting Your Own Location Information From Google - The Hard Way]]></title>
		<link>https://shkspr.mobi/blog/2014/04/extracting-your-own-location-information-from-google-the-hard-way/</link>
					<comments>https://shkspr.mobi/blog/2014/04/extracting-your-own-location-information-from-google-the-hard-way/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 28 Apr 2014 11:04:55 +0000</pubDate>
				<category><![CDATA[mobile]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[programming]]></category>
		<guid isPermaLink="false">http://shkspr.mobi/blog/?p=10378</guid>

					<description><![CDATA[Update! There&#039;s a new way to do this - read my latest post to find out how.    Two or three years ago, I was contacted by a recruiter from Google. They wanted to know if I&#039;d be interested in working for the advertising giant.  We played the usual game of dancing around salary and schedules, when he dropped the bombshell - they were looking for people to work on Google+.  I asked the recruiter if…]]></description>
										<content:encoded><![CDATA[<p><ins datetime="2015-09-02T12:17:20+00:00">Update!</ins> <a href="https://shkspr.mobi/blog/2015/09/get-your-google-location-history-the-hard-way-again/">There's a new way to do this - read my latest post to find out how</a>.</p>

<hr>

<p>Two or three years ago, I was contacted by a recruiter from Google. They wanted to know if I'd be interested in working for the advertising giant.</p>

<p>We played the usual game of dancing around salary and schedules, when he dropped the bombshell - they were looking for people to work on Google+.</p>

<p>I asked the recruiter if he'd seen my G+ profile.  At the time, it had this childish scrawl all over it.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2014/04/Google-Plus-UI-Crap-fs8.png" alt="Google Plus UI Crap" width="630" height="357" class="aligncenter size-full wp-image-10380">

<p>We mutually decided not to pursue the opportunity any further.</p>

<p>It's a decision I've often thought about. I've been involved in my fair share of high profile failures, and I didn't feel I could survive another one on my CV.&nbsp; Yet, I now wonder if I would have been able to do anything to improve the awful situation Google+ finds itself in with developers.</p>

<hr>

<h2 id="where-is-everybody"><a href="https://shkspr.mobi/blog/2014/04/extracting-your-own-location-information-from-google-the-hard-way/#where-is-everybody">Where Is Everybody?</a></h2>

<p>This weekend, I decided I wanted to build a <a href="http://harrypotter.wikia.com/wiki/Weasley_Clock">Weasley Family Clock</a>. You remember the one from Harry Potter which lists where every member of the family currently is?</p>

<p>During our last holiday to San Francisco, my wife and I had made extensive use of Google Latitude. She was alone in an unfamiliar city and I was stuck at a conference in the suburbs. It was a simple, passive, and comforting way to keep an eye on each other's whereabouts.</p>

<p>Well, <a href="https://web.archive.org/web/20140428030625/https://support.google.com/gmm/answer/3001634?hl=en-GB">Google killed Latitude</a>. Killed it stone dead. As part of their fanatical quest to drive people on to G+, they made that the only way to share the location data they were gathering.</p>

<p>So, my wife set up an account with the sole purpose of sharing her location with me. That's a +1 for their user numbers but -100 in good will.</p>

<p>Ok, so, let's get started!</p>

<h2 id="getting-location-data-through-the-google-api"><a href="https://shkspr.mobi/blog/2014/04/extracting-your-own-location-information-from-google-the-hard-way/#getting-location-data-through-the-google-api">Getting Location Data Through The Google+ API</a></h2>

<p>You can't.</p>

<p>Oh, don't get me wrong, you can <a href="https://developers.google.com/+/api/latest/people">read the documentation which says it's supported</a>, follow the <a href="https://developers.google.com/apis-explorer/#p/plus/v1/plus.people.get">tutorials</a> which show you how to do it, read <a href="https://stackoverflow.com/questions/17974426/google-api-get-profiles-currentlocation/21009160">StackOverflow posts giving you advice</a>, and <a href="https://www.youtube.com/watch?v=u1Qk076M9E8">watch YouTube videos showing how awesome it is</a> - but you can't get any location data out of the API. Not your friends', and not your own.</p>

<p>I'm a Product Manager. I know that sometimes you have to make tough choices about which new features to include, or which bugs to prioritise.</p>

<p>What I can't understand is how anyone has the gall to <a href="https://code.google.com/p/google-plus-platform/issues/detail?can=2&amp;start=0&amp;num=100&amp;q=&amp;colspec=ID%20Type%20Status%20Component%20Owner%20Summary%20Stars&amp;groupby=&amp;sort=&amp;id=620">actively solicit bug reports</a> from highly engaged members of the community, and then ignore them all.</p>

<p>I can't understand how you can have documentation which is <strong>fundamentally contradicted by reality</strong>.</p>

<p>I can't understand how any manager at Google thinks that annoying the people who want to interact with you is a good idea.</p>

<p>It's like Google were a toddler, repeatedly punching kids in the face and then wondering why they don't want to play with it.</p>

<p>All the people I know who work at Google are smart, passionate, committed, and eager to do the right thing. But something in that company is stopping them. The very culture of the company seems to be "we're the best of the best, therefore everything we do is perfect. There's no need to fix our broken windows."</p>

<p>I practice a zero-tolerance form of Product Management. Every small customer facing defect is a signal to your customers that you don't care about them. Each bug is a slap in the face and deserves a swift remedy - or at the very least an apology and a timescale for a fix.</p>

<p>Google is sticking its head in the sand and hoping developers will ignore the broken functionality, and broken promises, which now damage its reputation.</p>

<h2 id="how-to-liberate-your-location-data-from-google"><a href="https://shkspr.mobi/blog/2014/04/extracting-your-own-location-information-from-google-the-hard-way/#how-to-liberate-your-location-data-from-google">How To Liberate Your Location Data From Google</a></h2>

<p>You're going to be in for a fun ride - so strap in!</p>

<p>First, visit <a href="https://maps.google.com/locationhistory/b/0/">Google's Location History Page</a>.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2014/04/Google-Location-history-fs8.png" alt="Google Location history screenshot" width="624" height="540" class="aligncenter size-full wp-image-10386">

<p>On there, you'll see a link to download your location history by exporting it to KML. Go ahead and click it.</p>

<p>Hey presto! A KML file with your location throughout the day.</p>

<p>The URL format is:</p>

<pre>https://maps.google.com/locationhistory/b/0/kml?startTime=1398553200000&amp;endTime=1398639600000</pre>

<p>OK, so how do we get this programmatically?</p>

<p>Aha! <strong>Foolish grasshopper!</strong> Google doesn't provide <em>any</em> API to get this data. You <em>must</em> go through the Web interface.</p>

<p>Now, this is simply no good if we want real time access to our location. So, let's get creative.</p>

<p>If we have our Google cookies, we can retrieve our data fairly simply.</p>

<p>Using Chrome, <a href="https://chrome.google.com/webstore/detail/cookietxt-export/lopabhfecdfhgogdbojmaicoicjekelh">install this Cookie Exporting add on</a>.</p>

<p>In settings, allow it to access incognito windows.</p>

<p>Open an incognito window and log in to your Location History page.</p>

<p>Click the Cookie plugin, and you'll be rewarded with all your cookies for Google.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2014/04/Google-Cookies-Screenshot-fs8.png" alt="Google Cookies Screenshot" width="800" height="372" class="aligncenter size-full wp-image-10388">

<p>Copy and paste the data into a text file. I called mine "cookies.txt" because I'm an original and creative thinker.</p>

<p>To get your data using curl on the command line, run this.</p>

<pre>curl -b cookies.txt "https://maps.google.com/locationhistory/b/0/kml?startTime=1398510000000&amp;endTime=1398517922000"</pre>

<p>curl will use the cookies in the text file, and retrieve your information.</p>

<p>The eagle-eyed among you will have noticed that the start and end times will need to be updated. They are simply the Unix timestamps for the start and end of the day.</p>

<p>Here's how I calculated them using PHP.</p>

<pre lang="php">//  Today's time is needed to get the bounding for the request
$currentTime = time();

//  Beginning and end of the day
$startTime  = strtotime("midnight", $currentTime);
$endTime    = strtotime("tomorrow", $startTime);

//  The Google Location history URL - gets the KML.
$locationURL = "https://maps.google.com/locationhistory/b/0/kml?startTime=" .
                    $startTime . "000&amp;endTime=" .
                    $endTime . "000";
</pre>

<p>To request the file, using PHP, we need to tell curl where the cookies are.</p>

<pre lang="php">$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $locationURL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');

$kml = curl_exec($ch);
curl_close($ch);
</pre>

<p>All I want is the last geocoded point in the file, so there's no need to interpret it as XML (your needs may vary). So I just lazily iterate over each line, looking for the data I want.</p>

<pre lang="php">    //  Get the most recent location
    foreach(preg_split("/((r?n)|(rn?))/", $kml) as $line)
    {
        if (strrpos($line, "<gx:coord>") === 0)
        {
            //<gx:coord>-1.2345678 51.7654321 0</gx:coord>
            $line = str_replace("<gx:coord>", "", $line);
            $line = str_replace("</gx:coord>", "", $line);

            $pieces = explode(" ", $line);
            $long = $pieces[0];
            $lat = $pieces[1];
        }
    }
</gx:coord></pre>

<p>And there you have it. Stealing cookies, scraping data, and calculating timestamps - all because Google can't get its shit together.</p>

<p>This <em>should</em> have been a simple call according to their well documented (but incorrect) API.</p>

<p>Seriously Google, why should I recommend or use your products and services, when you treat me so disrespectfully?</p>

<h2 id="alternatives"><a href="https://shkspr.mobi/blog/2014/04/extracting-your-own-location-information-from-google-the-hard-way/#alternatives">Alternatives</a></h2>

<p>Some of you may be wondering why I didn't use a different product.</p>

<p>It's true that <a href="https://developer.glympse.com/">Glympse</a>, <a href="http://owntracks.org/">OwnTracks</a>, et al, do a pretty good job.</p>

<p>But that's <em>yet another </em>app to install, configure, update, maintain, and watch for battery life impact. I change phones regularly, and don't want the hassle of remembering another password to another service.</p>

<p>Others may question why I feel the need to track my wife's location all the time. It's simply a low effort way to ensure she knows when I'm stuck on the motorway, and I can see how long I have to clean the house and cook her dinner.</p>

<p>I'm building a dashboard to live by our front door which will give us an at-a-glance status update.</p>

<p>Hey, it works for us!</p>

<h2 id="a-final-message-to-google"><a href="https://shkspr.mobi/blog/2014/04/extracting-your-own-location-information-from-google-the-hard-way/#a-final-message-to-google">A Final Message To Google</a></h2>

<p>For the sake of my sanity - and the mental well-being of every developer who has to interact with you - please follow these simple guidelines.</p>

<ul>
    <li>Fix what is broken, before adding new functionality.</li>
    <li>Don't kill off a well used product without ensuring a robust transition plan.</li>
    <li>Make sure your documentation reflects reality.</li>
    <li>Acknowledge your bug reports - and act on them.</li>
</ul>

<p>Thanks!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=10378&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2014/04/extracting-your-own-location-information-from-google-the-hard-way/feed/</wfw:commentRss>
			<slash:comments>23</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Learning to Code vs Learning Computer Science]]></title>
		<link>https://shkspr.mobi/blog/2014/02/learning-to-code-vs-learning-computer-science/</link>
					<comments>https://shkspr.mobi/blog/2014/02/learning-to-code-vs-learning-computer-science/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 09 Feb 2014 10:51:17 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[appsforgood]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[schools]]></category>
		<category><![CDATA[yearofcode]]></category>
		<guid isPermaLink="false">http://shkspr.mobi/blog/?p=9837</guid>

					<description><![CDATA[It&#039;s always very tricky when people who aren&#039;t educators start banging on about what should or shouldn&#039;t be taught in schools.  My own school days are but a hazy memory of hormones, angst, and boring homework.  Yet here I am, pontificating.  With the current &#34;fad&#34; of encouraging children to learn to code, I thought I would be worth looking at the difference between coding and computer science.  …]]></description>
										<content:encoded><![CDATA[<p>It's always very tricky when people who aren't educators start banging on about what should or shouldn't be taught in schools.  My own school days are but a hazy memory of hormones, angst, and boring homework.  Yet here I am, pontificating.</p>

<p>With the current "fad" of <a href="https://web.archive.org/web/20140214101526/https://adrianshort.org/2014/02/09/lottie-dexter-quit-year-of-code/">encouraging children to learn to code</a>, I thought I would be worth looking at the difference between coding and computer science.
<a href="https://en.wikipedia.org/wiki/File:BBC_micro_assembly_listing.jpg"><img src="https://shkspr.mobi/blog/wp-content/uploads/2014/02/BBC-BASIC-Coding.png" alt=" Creative Commons Attribution-Share Alike 2.0 " width="600" height="250" class="aligncenter size-full wp-image-9841"></a></p>

<h2 id="history"><a href="https://shkspr.mobi/blog/2014/02/learning-to-code-vs-learning-computer-science/#history">History</a></h2>

<p>I learned the infamous Logo Turtle at school and BBC BASIC at home.  <em>That</em> is learning to code. I followed instructions, copy-typed from books, and made minor adjustments to "personalise" my programs.</p>

<p>At A-Level I started studying "<a href="https://web.archive.org/web/20140705120429/http://www.examsolutions.net/maths-revision/syllabuses/Edexcel/period-1/D1/module.php">Decision Mathematics</a>".  Rather than esoteric calculus, and prosaic formulas, it looks at how to solve problems logically.  It was the perfect primer for learning how to become a programmer and I firmly believe that no conversation about teaching coding in schools is complete without a theoretical understanding of the science behind the code.</p>

<h2 id="an-example"><a href="https://shkspr.mobi/blog/2014/02/learning-to-code-vs-learning-computer-science/#an-example">An Example</a></h2>

<p>This is an exercise I run through with people wanting to understand the basics of computer science:</p>

<p>Sort the following numbers:</p>

<pre>7, 8, 1, 3, 2, 7, 6</pre>

<p>Easy enough to do in your head, no?</p>

<p>Ok - <em>explain</em> to me how you did it?</p>

<p>We then talk through what we think the problem is - in what way do we want the numbers sorted? High to low? Low to high? Alphabetical?  What, in fact, do we want to do with the sorted numbers?</p>

<p>What series of very simple instructions could we give to an absolute idiot such that they could follow them and get the right answer?</p>

<p>Where are the "gotchas"?  If two numbers are the same, how should they be sorted?  Does it matter?</p>

<p>Are there more or less efficient ways to sort numbers?  Are there times when less efficiency is good?</p>

<p>Hey presto - we've begun to explore Computer <em>Science</em>.  We can craft algorithms, learn how and why they are important, write out pseudo-code, hunt for bugs.</p>

<h3 id="learning-to-code"><a href="https://shkspr.mobi/blog/2014/02/learning-to-code-vs-learning-computer-science/#learning-to-code">Learning To Code</a></h3>

<p>The above is a great introduction to Computer Science.  Now let's look at what learning to code teaches people.</p>

<pre lang="php">sort([7, 8, 1, 3, 2, 7, 6]);
</pre>

<p>Or, perhaps</p>

<pre lang="javascript">var numbers = [7, 8, 1, 3, 2, 7, 6];
numbers.sort();
</pre>

<p>What has that taught anyone?  Merely syntax.</p>

<p>I learned the BASIC and Pascal languages at school.  Who uses those now? No one.  Learning to code an iPhone or Android app is great fun - but in 10 years time when those children are adults, the world will have moved on and Objective C will be yet another obsolete language.</p>

<p>Yes, learning the rudiments of one language can be helpful in picking up another - but it's no substitute for understanding the fundamentals of <em>why</em> you are doing something.</p>

<p>That's why I'm a big supporter of the <a href="http://www.appsforgood.org/">Apps For Good</a> initiative.  Rather than just teaching kids how to build an Android app, it takes them through the process of design, user research, competitor analysis, testing, monetizing, building a team, and legal / social ramifications of releasing an app.  All of which are vital tools to any would-be programmer.</p>

<p>It's not just about learning which buttons to press in order to make an app - it's about a deeper understanding.</p>

<h2 id="if-thou-summers_day-darling_buds-windsrough-shake"><a href="https://shkspr.mobi/blog/2014/02/learning-to-code-vs-learning-computer-science/#if-thou-summers_day-darling_buds-windsrough-shake">if ($thou &gt;= $summers_day) {$darling_buds.winds($rough, $shake);}</a></h2>

<p>Let's put it in language the humanities graduates can understand.</p>

<p><strong>Learning to code is merely teaching people to spell.</strong></p>

<p>Computer Science is about what makes a poem beautiful, why alliteration is alluring, how iambic pentameter unlocks the secrets of Shakespeare.</p>

<p><strong>That</strong> is what I think we need to be teaching.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=9837&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2014/02/learning-to-code-vs-learning-computer-science/feed/</wfw:commentRss>
			<slash:comments>33</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Dave Winer is Wrong About Hackdays]]></title>
		<link>https://shkspr.mobi/blog/2012/02/dave-winer-is-wrong-about-hackdays/</link>
					<comments>https://shkspr.mobi/blog/2012/02/dave-winer-is-wrong-about-hackdays/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 21 Feb 2012 09:55:41 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[hackathon]]></category>
		<category><![CDATA[hackday]]></category>
		<category><![CDATA[winer]]></category>
		<guid isPermaLink="false">http://shkspr.mobi/blog/?p=5360</guid>

					<description><![CDATA[Dave Winer is totally off base when he says &#34;Hackathons are how marketing guys wish software were made.&#34;  Perhaps it&#039;s different in his part of the world, but over here, hackdays are fun!  All the hackathon / hackdays I&#039;ve been to are, essentially, Scrapheap Challenge for software people.  The last hackday I went to was about people learning, playing, relaxing, building, tinkering, bodging,…]]></description>
										<content:encoded><![CDATA[<p>Dave Winer is totally off base when he says "<a href="http://scripting.com/stories/2012/02/19/hackathonsAreNonsense.html">Hackathons are how marketing guys wish software were made</a>."</p>

<p>Perhaps it's different in his part of the world, but over here, hackdays are <em>fun</em>!</p>

<p>All the hackathon / hackdays I've been to are, essentially, Scrapheap Challenge<sup id="fnref:junk"><a href="https://shkspr.mobi/blog/2012/02/dave-winer-is-wrong-about-hackdays/#fn:junk" class="footnote-ref" title="Note to Americans, Junkyard Wars is the US remake of Scrapheap Challenge." role="doc-noteref">0</a></sup> for software people.</p>

<p>The last hackday I went to was about people learning, playing, relaxing, building, tinkering, bodging, faking, and innovating.</p>

<p>All without a design document, manager, brand police, or scrum master.  And here's the thing - no one was expecting quality software.  No one was expecting amazing things.  No marketing guys (that's me!) thought "I wonder how we can leverage the synergies of the cross-cultural blahblahblahblahblahblah..."</p>

<p>And yet... Quality software got made.  Innovation happened.  Very little of it had usability, security, and scalability baked into it.  But just like an artist's sketch can become a masterwork, or a film-maker's short can be developed into a blockbuster, the kernels of great software was laid down.</p>

<p>I don't know how many managers or marketing guys actually attend hackathons usually, but if they'd attened the one I went to at the weekend, they would have taken away a very important lesson.</p>

<p>Every team had to present what they'd built.  Some had fully working software, others had prototypes, a few had a series of screenshots.</p>

<p>One team came up and showed their project plan!  Seriously!  All they had was a bit of paper showing the roles they were assigned, the plan for what each person was to do, a few vauge user flow diagrams, and no software.</p>

<p>It was plain to see that a hackday <strong>isn't</strong> how <em>good</em> software gets made.  It's not even the <em>ideal</em> way to make software.</p>

<p>It's just <strong>a</strong> way to make software.  The inventions on Scrapheap Challenge would never be released to the public after a weekend with a blow-torch - and yet some of the software built at the hackday was useful and ready to be released the next day.</p>

<p>With all the tools available to the competent programmer, it's now incredibly easy to write useful, scalable, high quality, innovative, beautiful, creative, playful, and helpful software in a weekend.</p>

<p>Or, you can just build a text to speech service which randomly calls your phone and says rude words to you.</p>

<p>Hackdays are about fun.</p>

<div id="footnotes" role="doc-endnotes">
<hr>
<ol start="0">

<li id="fn:junk">
<p>Note to Americans, Junkyard Wars is the US remake of Scrapheap Challenge.&nbsp;<a href="https://shkspr.mobi/blog/2012/02/dave-winer-is-wrong-about-hackdays/#fnref:junk" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=5360&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2012/02/dave-winer-is-wrong-about-hackdays/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Displaying Twitter Photos via Entities]]></title>
		<link>https://shkspr.mobi/blog/2011/06/displaying-twitter-photos-via-entities/</link>
					<comments>https://shkspr.mobi/blog/2011/06/displaying-twitter-photos-via-entities/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 03 Jun 2011 15:43:19 +0000</pubDate>
				<category><![CDATA[mobile]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[dabr]]></category>
		<category><![CDATA[HowTo]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[twitter]]></category>
		<guid isPermaLink="false">http://shkspr.mobi/blog/?p=4150</guid>

					<description><![CDATA[Twitter has announced that it will soon open up a native photo sharing service.  Rather than using an external service like Embed.ly to retrieve thumbnails, all the data is embedded within Twitter Entities.  So, if you request a status using &#34;include_entities=true&#34;, you will be able to grab the image and display the thumbnail using the following code.  function twitter_get_media($status) {   …]]></description>
										<content:encoded><![CDATA[<p>Twitter has announced that it will soon open up a <a href="http://groups.google.com/group/twitter-development-talk/browse_thread/thread/99b451ce5d8259ce#">native photo sharing service</a>.</p>

<p>Rather than using an external service like Embed.ly to retrieve thumbnails, all the data is embedded within <a href="https://web.archive.org/web/20110611123536/http://dev.twitter.com/pages/tweet_entities">Twitter Entities</a>.</p>

<p>So, if you request a status using "<em>include_entities=true</em>", you will be able to grab the image and display the thumbnail using the following code.</p>

<pre><code class="language-php">function twitter_get_media($status) {
   if($status-&gt;entities-&gt;media) {

      $url = $status-&gt;entities-&gt;media[0]-&gt;media_url_https;

      $width = $status-&gt;entities-&gt;media[0]-&gt;sizes-&gt;thumb-&gt;w;
      $height = $status-&gt;entities-&gt;media[0]-&gt;sizes-&gt;thumb-&gt;h;

      $media_html = "&lt;a href="" . $url . "" target='_blank'&gt;";
      $media_html .=  "&lt;img src="" . $url . ":thumb" width="" . $width .
         "" height="" . $height . "" /&gt;";
      $media_html .= "&lt;/a&gt;&lt;br /&gt;";

      return $media_html;
   }
}
</code></pre>

<p>So, a tweet like this:
<a href="https://web.archive.org/web/20181224040939/https://twitter.com/twitter/status/76360760606986241">https://twitter.com/twitter/status/76360760606986241</a>
Will render like this (in Dabr):
<img src="https://shkspr.mobi/blog/wp-content/uploads/2011/06/Twitter-Dabr-Images.jpg" alt="Twitter Dabr Images" title="Twitter Dabr Images" width="317" height="528" class="aligncenter size-full wp-image-4154"></p>

<h2 id="notes"><a href="https://shkspr.mobi/blog/2011/06/displaying-twitter-photos-via-entities/#notes">Notes</a></h2>

<p>This is very rough and ready proof of concept code.  Beware of the following:</p>

<ul>
    <li>This will only take the first image from the tweet.</li>
    <li>Only images are supported - I'm not sure how their proposed video sharing will work.</li>
    <li>There's no error checking.</li>
    <li>In the above code, the https URL is used - if you want a non-SSL link, you'll need to remove the "_https"</li>
</ul>

<p>Enjoy!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=4150&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2011/06/displaying-twitter-photos-via-entities/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
	</channel>
</rss>
